<!DOCTYPE html><html lang="zh-CN" data-theme="light"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0"><title>Spring高级45讲【第五章】：BOOT | 狼族少年、血狼</title><meta name="author" content="狼族少年、血狼"><meta name="copyright" content="狼族少年、血狼"><meta name="format-detection" content="telephone=no"><meta name="theme-color" content="#ffffff"><meta name="description" content="构建Boot项目传统方式生成使用IntelliJ IDEA软件，点击文件-&gt;新建-&gt;项目  左侧选择Spring Initializr，右侧设置项目信息，点击下一步  选择Spring Boot版本，添加依赖  点击创建  SpringBoot项目构建成功，但是我们发现项目中会有很多额外的文件，例如.mvn文件夹、mvnw、mvnw.cmd等文件。 快捷方式生成使用以下命令获取pom.">
<meta property="og:type" content="article">
<meta property="og:title" content="Spring高级45讲【第五章】：BOOT">
<meta property="og:url" content="https://geekwolfman.github.io/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT.html">
<meta property="og:site_name" content="狼族少年、血狼">
<meta property="og:description" content="构建Boot项目传统方式生成使用IntelliJ IDEA软件，点击文件-&gt;新建-&gt;项目  左侧选择Spring Initializr，右侧设置项目信息，点击下一步  选择Spring Boot版本，添加依赖  点击创建  SpringBoot项目构建成功，但是我们发现项目中会有很多额外的文件，例如.mvn文件夹、mvnw、mvnw.cmd等文件。 快捷方式生成使用以下命令获取pom.">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/01cover.jpg">
<meta property="article:published_time" content="2023-04-15T14:40:40.000Z">
<meta property="article:modified_time" content="2023-04-15T15:06:58.237Z">
<meta property="article:author" content="狼族少年、血狼">
<meta property="article:tag" content="spring">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/01cover.jpg"><link rel="shortcut icon" href="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/config/avatar/avatar.png"><link rel="canonical" href="https://geekwolfman.github.io/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT.html"><link rel="preconnect" href="//cdn.jsdelivr.net"/><link rel="preconnect" href="//busuanzi.ibruce.info"/><link rel="stylesheet" href="/css/index.css"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css" media="print" onload="this.media='all'"><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.min.css" media="print" onload="this.media='all'"><script>const GLOBAL_CONFIG = { 
  root: '/',
  algolia: undefined,
  localSearch: {"path":"/db.json","preload":false,"languages":{"hits_empty":"找不到您查询的内容：${query}"}},
  translate: undefined,
  noticeOutdate: undefined,
  highlight: {"plugin":"highlighjs","highlightCopy":true,"highlightLang":true,"highlightHeightLimit":false},
  copy: {
    success: '复制成功',
    error: '复制错误',
    noSupport: '浏览器不支持'
  },
  relativeDate: {
    homepage: false,
    post: false
  },
  runtime: '天',
  date_suffix: {
    just: '刚刚',
    min: '分钟前',
    hour: '小时前',
    day: '天前',
    month: '个月前'
  },
  copyright: undefined,
  lightbox: 'fancybox',
  Snackbar: undefined,
  source: {
    justifiedGallery: {
      js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.js',
      css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery/dist/fjGallery.min.css'
    }
  },
  isPhotoFigcaption: false,
  islazyload: false,
  isAnchor: false,
  percent: {
    toc: true,
    rightside: true,
  }
}</script><script id="config-diff">var GLOBAL_CONFIG_SITE = {
  title: 'Spring高级45讲【第五章】：BOOT',
  isPost: true,
  isHome: false,
  isHighlightShrink: false,
  isToc: true,
  postUpdate: '2023-04-15 23:06:58'
}</script><noscript><style type="text/css">
  #nav {
    opacity: 1
  }
  .justified-gallery img {
    opacity: 1
  }

  #recent-posts time,
  #post-meta time {
    display: inline !important
  }
</style></noscript><script>(win=>{
    win.saveToLocal = {
      set: function setWithExpiry(key, value, ttl) {
        if (ttl === 0) return
        const now = new Date()
        const expiryDay = ttl * 86400000
        const item = {
          value: value,
          expiry: now.getTime() + expiryDay,
        }
        localStorage.setItem(key, JSON.stringify(item))
      },

      get: function getWithExpiry(key) {
        const itemStr = localStorage.getItem(key)

        if (!itemStr) {
          return undefined
        }
        const item = JSON.parse(itemStr)
        const now = new Date()

        if (now.getTime() > item.expiry) {
          localStorage.removeItem(key)
          return undefined
        }
        return item.value
      }
    }
  
    win.getScript = url => new Promise((resolve, reject) => {
      const script = document.createElement('script')
      script.src = url
      script.async = true
      script.onerror = reject
      script.onload = script.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        script.onload = script.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(script)
    })
  
    win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
      const link = document.createElement('link')
      link.rel = 'stylesheet'
      link.href = url
      if (id) link.id = id
      link.onerror = reject
      link.onload = link.onreadystatechange = function() {
        const loadState = this.readyState
        if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
        link.onload = link.onreadystatechange = null
        resolve()
      }
      document.head.appendChild(link)
    })
  
      win.activateDarkMode = function () {
        document.documentElement.setAttribute('data-theme', 'dark')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#0d0d0d')
        }
      }
      win.activateLightMode = function () {
        document.documentElement.setAttribute('data-theme', 'light')
        if (document.querySelector('meta[name="theme-color"]') !== null) {
          document.querySelector('meta[name="theme-color"]').setAttribute('content', '#ffffff')
        }
      }
      const t = saveToLocal.get('theme')
    
          if (t === 'dark') activateDarkMode()
          else if (t === 'light') activateLightMode()
        
      const asideStatus = saveToLocal.get('aside-status')
      if (asideStatus !== undefined) {
        if (asideStatus === 'hide') {
          document.documentElement.classList.add('hide-aside')
        } else {
          document.documentElement.classList.remove('hide-aside')
        }
      }
    
    const detectApple = () => {
      if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
        document.documentElement.classList.add('apple')
      }
    }
    detectApple()
    })(window)</script><meta name="generator" content="Hexo 6.3.0"></head><body><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pace-js/themes/blue/pace-theme-minimal.min.css"/><script src="https://cdn.jsdelivr.net/npm/pace-js/pace.min.js"></script><div id="sidebar"><div id="menu-mask"></div><div id="sidebar-menus"><div class="avatar-img is-center"><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/config/avatar/avatar.png" onerror="onerror=null;src='/img/friend_404.gif'" alt="avatar"/></div><div class="sidebar-site-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">57</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">14</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">9</div></a></div><hr/><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/gallery/"><i class="fa-fw fas fa-images"></i><span> 画廊</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-paper-plane"></i><span> 关于</span></a></div></div></div></div><div class="post" id="body-wrap"><header class="post-bg" id="page-header" style="background-image: url('https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/01cover.jpg')"><nav id="nav"><span id="blog-info"><a href="/" title="狼族少年、血狼"><span class="site-name">狼族少年、血狼</span></a></span><div id="menus"><div id="search-button"><a class="site-page social-icon search" href="javascript:void(0);"><i class="fas fa-search fa-fw"></i><span> 搜索</span></a></div><div class="menus_items"><div class="menus_item"><a class="site-page" href="/"><i class="fa-fw fas fa-home"></i><span> 首页</span></a></div><div class="menus_item"><a class="site-page" href="/categories/"><i class="fa-fw fas fa-folder-open"></i><span> 分类</span></a></div><div class="menus_item"><a class="site-page" href="/tags/"><i class="fa-fw fas fa-tags"></i><span> 标签</span></a></div><div class="menus_item"><a class="site-page" href="/archives/"><i class="fa-fw fas fa-archive"></i><span> 归档</span></a></div><div class="menus_item"><a class="site-page" href="/gallery/"><i class="fa-fw fas fa-images"></i><span> 画廊</span></a></div><div class="menus_item"><a class="site-page" href="/link/"><i class="fa-fw fas fa-link"></i><span> 友链</span></a></div><div class="menus_item"><a class="site-page" href="/about/"><i class="fa-fw fas fa-paper-plane"></i><span> 关于</span></a></div></div><div id="toggle-menu"><a class="site-page" href="javascript:void(0);"><i class="fas fa-bars fa-fw"></i></a></div></div></nav><div id="post-info"><h1 class="post-title">Spring高级45讲【第五章】：BOOT</h1><div id="post-meta"><div class="meta-firstline"><span class="post-meta-date"><i class="far fa-calendar-alt fa-fw post-meta-icon"></i><span class="post-meta-label">发表于</span><time class="post-meta-date-created" datetime="2023-04-15T14:40:40.000Z" title="发表于 2023-04-15 22:40:40">2023-04-15</time><span class="post-meta-separator">|</span><i class="fas fa-history fa-fw post-meta-icon"></i><span class="post-meta-label">更新于</span><time class="post-meta-date-updated" datetime="2023-04-15T15:06:58.237Z" title="更新于 2023-04-15 23:06:58">2023-04-15</time></span><span class="post-meta-categories"><span class="post-meta-separator">|</span><i class="fas fa-inbox fa-fw post-meta-icon"></i><a class="post-meta-categories" href="/categories/%E5%8E%9F%E7%90%86%E6%8E%A2%E7%A9%B6/">原理探究</a></span></div><div class="meta-secondline"><span class="post-meta-separator">|</span><span class="post-meta-wordcount"><i class="far fa-file-word fa-fw post-meta-icon"></i><span class="post-meta-label">字数总计:</span><span class="word-count">21.9k</span><span class="post-meta-separator">|</span><i class="far fa-clock fa-fw post-meta-icon"></i><span class="post-meta-label">阅读时长:</span><span>114分钟</span></span><span class="post-meta-separator">|</span><span class="post-meta-pv-cv" id="" data-flag-title="Spring高级45讲【第五章】：BOOT"><i class="far fa-eye fa-fw post-meta-icon"></i><span class="post-meta-label">阅读量:</span><span id="busuanzi_value_page_pv"><i class="fa-solid fa-spinner fa-spin"></i></span></span></div></div></div></header><main class="layout" id="content-inner"><div id="post"><article class="post-content" id="article-container"><h1 id="构建Boot项目"><a href="#构建Boot项目" class="headerlink" title="构建Boot项目"></a>构建Boot项目</h1><h2 id="传统方式生成"><a href="#传统方式生成" class="headerlink" title="传统方式生成"></a>传统方式生成</h2><p>使用<code>IntelliJ IDEA</code>软件，点击文件-&gt;新建-&gt;项目</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/02.png" alt="img"></p>
<p>左侧选择<code>Spring Initializr</code>，右侧设置项目信息，点击下一步</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/03.png" alt="img"></p>
<p>选择<code>Spring Boot</code>版本，添加依赖</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/04.png" alt="img"></p>
<p>点击创建</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/05.png" alt="img"></p>
<p><code>SpringBoot</code>项目构建成功，但是我们发现项目中会有很多额外的文件，例如.mvn文件夹、mvnw、mvnw.cmd等文件。</p>
<h2 id="快捷方式生成"><a href="#快捷方式生成" class="headerlink" title="快捷方式生成"></a>快捷方式生成</h2><p>使用以下命令获取<code>pom.xml</code>：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl https://start.spring.io/pom.xml</span><br></pre></td></tr></table></figure>

<p>控制台输出：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">    <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-parent<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0.5<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">relativePath</span>/&gt;</span> <span class="comment">&lt;!-- lookup parent from repository --&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.example<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>demo<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.0.1-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">name</span>&gt;</span>demo<span class="tag">&lt;/<span class="name">name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">description</span>&gt;</span>Demo project for Spring Boot<span class="tag">&lt;/<span class="name">description</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">java.version</span>&gt;</span>17<span class="tag">&lt;/<span class="name">java.version</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line"></span><br><span class="line">        <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-starter-test<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">                <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>如果需要添加依赖，我们可以使用<code>-d</code>选项：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl https://start.spring.io/pom.xml -d dependencies=mysql,mybatis,web</span><br></pre></td></tr></table></figure>

<p>如果想要存成文件，可以使用<code>-o</code>选项：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl https://start.spring.io/pom.xml -d dependencies=mysql,mybatis,web -o pom.xml</span><br></pre></td></tr></table></figure>

<p>如果想要查看更多使用方式，可以访问<a target="_blank" rel="noopener" href="https://start.spring.io/">https://start.spring.io</a></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br></pre></td><td class="code"><pre><span class="line">curl https://start.spring.io</span><br><span class="line"> .   ____          _            __ _ _</span><br><span class="line"> /\\ / ___&#x27;_ __ _ _(_)_ __  __ _ \ \ \ \</span><br><span class="line">( ( )\___ | &#x27;_ | &#x27;_| | &#x27;_ \/ _` | \ \ \ \</span><br><span class="line"> \\/  ___)| |_)| | | | | || (_| |  ) ) ) )</span><br><span class="line">  &#x27;  |____| .__|_| |_|_| |_\__, | / / / /</span><br><span class="line"> =========|_|==============|___/=/_/_/_/</span><br><span class="line">:: Spring Initializr ::  https://start.spring.io</span><br><span class="line"></span><br><span class="line">This service generates quickstart projects that can be easily customized.</span><br><span class="line">Possible customizations include a project&#x27;s dependencies, Java version, and</span><br><span class="line">build system or build structure. See below for further details.</span><br><span class="line"></span><br><span class="line">The services uses a HAL based hypermedia format to expose a set of resources</span><br><span class="line">to interact with. If you access this root resource requesting application/json</span><br><span class="line">as media type the response will contain the following links:</span><br><span class="line">+-----------------------+--------------------------------------------------------------+</span><br><span class="line">| Rel                   | Description                                                  |</span><br><span class="line">+-----------------------+--------------------------------------------------------------+</span><br><span class="line">| gradle-build          | Generate a Gradle build file.                                |</span><br><span class="line">|                       |                                                              |</span><br><span class="line">| gradle-project *      | Generate a Gradle based project archive using the Groovy     |</span><br><span class="line">|                       | DSL.                                                         |</span><br><span class="line">|                       |                                                              |</span><br><span class="line">| gradle-project-kotlin | Generate a Gradle based project archive using the Kotlin     |</span><br><span class="line">|                       | DSL.                                                         |</span><br><span class="line">|                       |                                                              |</span><br><span class="line">| maven-build           | Generate a Maven pom.xml.                                    |</span><br><span class="line">|                       |                                                              |</span><br><span class="line">| maven-project         | Generate a Maven based project archive.                      |</span><br><span class="line">+-----------------------+--------------------------------------------------------------+</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">The URI templates take a set of parameters to customize the result of a request</span><br><span class="line">to the linked resource.</span><br><span class="line">+-----------------+------------------------------------------+------------------------------+</span><br><span class="line">| Parameter       | Description                              | Default value                |</span><br><span class="line">+-----------------+------------------------------------------+------------------------------+</span><br><span class="line">| applicationName | application name                         | DemoApplication              |</span><br><span class="line">| artifactId      | project coordinates (infer archive name) | demo                         |</span><br><span class="line">| baseDir         | base directory to create in the archive  | no base dir                  |</span><br><span class="line">| bootVersion     | spring boot version                      | 3.0.5                        |</span><br><span class="line">| dependencies    | dependency identifiers (comma-separated) | none                         |</span><br><span class="line">| description     | project description                      | Demo project for Spring Boot |</span><br><span class="line">| groupId         | project coordinates                      | com.example                  |</span><br><span class="line">| javaVersion     | language level                           | 17                           |</span><br><span class="line">| language        | programming language                     | java                         |</span><br><span class="line">| name            | project name (infer application name)    | demo                         |</span><br><span class="line">| packageName     | root package                             | com.example.demo             |</span><br><span class="line">| packaging       | project packaging                        | jar                          |</span><br><span class="line">| type            | project type                             | gradle-project               |</span><br><span class="line">| version         | project version                          | 0.0.1-SNAPSHOT               |</span><br><span class="line">+-----------------+------------------------------------------+------------------------------+</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">The following section has a list of supported identifiers for the comma-separated</span><br><span class="line">list of &quot;dependencies&quot;.</span><br><span class="line">+--------------------------------------+--------------------------------------------------------------+-------------------------------+</span><br><span class="line">| Id                                   | Description                                                  | Required version              |</span><br><span class="line">+--------------------------------------+--------------------------------------------------------------+-------------------------------+</span><br><span class="line">| activemq                             | Spring JMS support with Apache ActiveMQ &#x27;Classic&#x27;.           | &gt;=2.0.0.RELEASE and &lt;3.0.0-M1 |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| actuator                             | Supports built in (or custom) endpoints that let you monitor |                               |</span><br><span class="line">|                                      | and manage your application - such as application health,    |                               |</span><br><span class="line">|                                      | metrics, sessions, etc.                                      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| amqp                                 | Gives your applications a common platform to send and        |                               |</span><br><span class="line">|                                      | receive messages, and your messages a safe place to live     |                               |</span><br><span class="line">|                                      | until received.                                              |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| artemis                              | Spring JMS support with Apache ActiveMQ Artemis.             |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| azure-active-directory               | Spring Security integration with Azure Active Directory for  | &gt;=2.5.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | authentication.                                              |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| azure-cosmos-db                      | Fully managed NoSQL database service for modern app          | &gt;=2.5.0-M1 and &lt;3.0.0-M1      |</span><br><span class="line">|                                      | development, including Spring Data support.                  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| azure-keyvault                       | All key vault features are supported, e.g. manage            | &gt;=2.5.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | application secrets and certificates.                        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| azure-storage                        | All Storage features are supported, e.g. blob, fileshare and | &gt;=2.5.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | queue.                                                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| azure-support                        | Auto-configuration for Azure Services (Service Bus, Storage, | &gt;=2.5.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | Active Directory, Key Vault, and more).                      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| batch                                | Batch applications with transactions, retry/skip and chunk   |                               |</span><br><span class="line">|                                      | based processing.                                            |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cache                                | Provides cache-related operations, such as the ability to    |                               |</span><br><span class="line">|                                      | update the content of the cache, but does not provide the    |                               |</span><br><span class="line">|                                      | actual data store.                                           |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| camel                                | Apache Camel is an open source integration framework that    | &gt;=2.0.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | empowers you to quickly and easily integrate various systems |                               |</span><br><span class="line">|                                      | consuming or producing data.                                 |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-bus                            | Links nodes of a distributed system with a lightweight       | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | message broker which can used to broadcast state changes or  |                               |</span><br><span class="line">|                                      | other management instructions (requires a binder, e.g.       |                               |</span><br><span class="line">|                                      | Apache Kafka or RabbitMQ).                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-cloudfoundry-discovery         | Service discovery with Cloud Foundry.                        | &gt;=2.3.0.M1 and &lt;3.0.0-M1      |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-config-client                  | Client that connects to a Spring Cloud Config Server to      | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | fetch the application&#x27;s configuration.                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-config-server                  | Central management for configuration via Git, SVN, or        | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | HashiCorp Vault.                                             |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-contract-stub-runner           | Stub Runner for HTTP/Messaging based communication. Allows   | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | creating WireMock stubs from RestDocs tests.                 |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-contract-verifier              | Moves TDD to the level of software architecture by enabling  | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | Consumer Driven Contract (CDC) development.                  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-eureka                         | A REST based service for locating services for the purpose   | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | of load balancing and failover of middle-tier servers.       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-eureka-server                  | spring-cloud-netflix Eureka Server.                          | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-feign                          | Declarative REST Client. OpenFeign creates a dynamic         | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | implementation of an interface decorated with JAX-RS or      |                               |</span><br><span class="line">|                                      | Spring MVC annotations.                                      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-function                       | Promotes the implementation of business logic via functions  | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | and supports a uniform programming model across serverless   |                               |</span><br><span class="line">|                                      | providers, as well as the ability to run standalone (locally |                               |</span><br><span class="line">|                                      | or in a PaaS).                                               |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-gateway                        | Provides a simple, yet effective way to route to APIs and    | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | provide cross cutting concerns to them such as security,     |                               |</span><br><span class="line">|                                      | monitoring/metrics, and resiliency.                          |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-gcp                            | Contains auto-configuration support for every Spring Cloud   | &gt;=2.4.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | GCP integration. Most of the auto-configuration code is only |                               |</span><br><span class="line">|                                      | enabled if other dependencies are added to the classpath.    |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-gcp-pubsub                     | Adds the GCP Support entry and all the required dependencies | &gt;=2.4.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | so that the Google Cloud Pub/Sub integration work out of the |                               |</span><br><span class="line">|                                      | box.                                                         |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-gcp-storage                    | Adds the GCP Support entry and all the required dependencies | &gt;=2.4.0-M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | so that the Google Cloud Storage integration work out of the |                               |</span><br><span class="line">|                                      | box.                                                         |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-loadbalancer                   | Client-side load-balancing with Spring Cloud LoadBalancer.   | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-resilience4j                   | Spring Cloud Circuit breaker with Resilience4j as the        | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | underlying implementation.                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-starter                        | Non-specific Spring Cloud features, unrelated to external    | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | libraries or integrations (e.g. Bootstrap context and        |                               |</span><br><span class="line">|                                      | @RefreshScope).                                              |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-starter-consul-config          | Enable and configure the common patterns inside your         | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | application and build large distributed systems with         |                               |</span><br><span class="line">|                                      | Hashicorp’s Consul. The patterns provided include Service    |                               |</span><br><span class="line">|                                      | Discovery, Distributed Configuration and Control Bus.        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-starter-consul-discovery       | Service discovery with Hashicorp Consul.                     | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-starter-vault-config           | Provides client-side support for externalized configuration  | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | in a distributed system. Using HashiCorp&#x27;s Vault you have a  |                               |</span><br><span class="line">|                                      | central place to manage external secret properties for       |                               |</span><br><span class="line">|                                      | applications across all environments.                        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-starter-zookeeper-config       | Enable and configure common patterns inside your application | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | and build large distributed systems with Apache Zookeeper    |                               |</span><br><span class="line">|                                      | based components. The provided patterns include Service      |                               |</span><br><span class="line">|                                      | Discovery and Configuration.                                 |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-starter-zookeeper-discovery    | Service discovery with Apache Zookeeper.                     | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-stream                         | Framework for building highly scalable event-driven          | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | microservices connected with shared messaging systems        |                               |</span><br><span class="line">|                                      | (requires a binder, e.g. Apache Kafka, Apache Pulsar,        |                               |</span><br><span class="line">|                                      | RabbitMQ, or Solace PubSub+).                                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| cloud-task                           | Allows a user to develop and run short lived microservices   | &gt;=2.3.0.M1 and &lt;3.1.0-M1      |</span><br><span class="line">|                                      | using Spring Cloud. Run them locally, in the cloud, and on   |                               |</span><br><span class="line">|                                      | Spring Cloud Data Flow.                                      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| codecentric-spring-boot-admin-client | Required for your application to register with a             | &gt;=2.0.0.RELEASE and &lt;3.1.0-M1 |</span><br><span class="line">|                                      | Codecentric&#x27;s Spring Boot Admin Server instance.             |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| codecentric-spring-boot-admin-server | A community project to manage and monitor your Spring Boot   | &gt;=2.0.0.RELEASE and &lt;3.1.0-M1 |</span><br><span class="line">|                                      | applications. Provides a UI on top of the Spring Boot        |                               |</span><br><span class="line">|                                      | Actuator endpoints.                                          |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| configuration-processor              | Generate metadata for developers to offer contextual help    |                               |</span><br><span class="line">|                                      | and &quot;code completion&quot; when working with custom configuration |                               |</span><br><span class="line">|                                      | keys (ex.application.properties/.yml files).                 |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-cassandra                       | A free and open-source, distributed, NoSQL database          |                               |</span><br><span class="line">|                                      | management system that offers high-scalability and           |                               |</span><br><span class="line">|                                      | high-performance.                                            |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-cassandra-reactive              | Access Cassandra NoSQL Database in a reactive fashion.       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-couchbase                       | NoSQL document-oriented database that offers in memory-first |                               |</span><br><span class="line">|                                      | architecture, geo-distributed deployments, and workload      |                               |</span><br><span class="line">|                                      | isolation.                                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-couchbase-reactive              | Access Couchbase NoSQL database in a reactive fashion with   |                               |</span><br><span class="line">|                                      | Spring Data Couchbase.                                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-elasticsearch                   | A distributed, RESTful search and analytics engine with      |                               |</span><br><span class="line">|                                      | Spring Data Elasticsearch.                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-jdbc                            | Persist data in SQL stores with plain JDBC using Spring      |                               |</span><br><span class="line">|                                      | Data.                                                        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-jpa                             | Persist data in SQL stores with Java Persistence API using   |                               |</span><br><span class="line">|                                      | Spring Data and Hibernate.                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-ldap                            | Makes it easier to build Spring based applications that use  |                               |</span><br><span class="line">|                                      | the Lightweight Directory Access Protocol.                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-mongodb                         | Store data in flexible, JSON-like documents, meaning fields  |                               |</span><br><span class="line">|                                      | can vary from document to document and data structure can be |                               |</span><br><span class="line">|                                      | changed over time.                                           |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-mongodb-reactive                | Provides asynchronous stream processing with non-blocking    |                               |</span><br><span class="line">|                                      | back pressure for MongoDB.                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-neo4j                           | An open source NoSQL database that stores data structured as |                               |</span><br><span class="line">|                                      | graphs consisting of nodes, connected by relationships.      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-r2dbc                           | Provides Reactive Relational Database Connectivity to        |                               |</span><br><span class="line">|                                      | persist data in SQL stores using Spring Data in reactive     |                               |</span><br><span class="line">|                                      | applications.                                                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-redis                           | Advanced and thread-safe Java Redis client for synchronous,  |                               |</span><br><span class="line">|                                      | asynchronous, and reactive usage. Supports Cluster,          |                               |</span><br><span class="line">|                                      | Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-redis-reactive                  | Access Redis key-value data stores in a reactive fashion     |                               |</span><br><span class="line">|                                      | with Spring Data Redis.                                      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-rest                            | Exposing Spring Data repositories over REST via Spring Data  |                               |</span><br><span class="line">|                                      | REST.                                                        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| data-rest-explorer                   | Browsing Spring Data REST repositories in your browser.      |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| datadog                              | Publish Micrometer metrics to Datadog, a dimensional         |                               |</span><br><span class="line">|                                      | time-series SaaS with built-in dashboarding and alerting.    |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| db2                                  | A JDBC driver that provides access to IBM DB2.               | &gt;=2.2.0.M6                    |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| derby                                | An open source relational database implemented entirely in   |                               |</span><br><span class="line">|                                      | Java.                                                        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| devtools                             | Provides fast application restarts, LiveReload, and          |                               |</span><br><span class="line">|                                      | configurations for enhanced development experience.          |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| distributed-tracing                  | Enable span and trace IDs in logs.                           |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| flapdoodle-mongo                     | Provides a platform neutral way for running MongoDB in unit  | &gt;=2.0.0.RELEASE and &lt;3.0.0-M1 |</span><br><span class="line">|                                      | tests.                                                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| flyway                               | Version control for your database so you can migrate from    |                               |</span><br><span class="line">|                                      | any version (incl. an empty database) to the latest version  |                               |</span><br><span class="line">|                                      | of the schema.                                               |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| freemarker                           | Java library to generate text output (HTML web pages,        |                               |</span><br><span class="line">|                                      | e-mails, configuration files, source code, etc.) based on    |                               |</span><br><span class="line">|                                      | templates and changing data.                                 |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| graphite                             | Publish Micrometer metrics to Graphite, a hierarchical       |                               |</span><br><span class="line">|                                      | metrics system backed by a fixed-size database.              |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| graphql                              | Build GraphQL applications with Spring for GraphQL and       | &gt;=2.7.0.M1                    |</span><br><span class="line">|                                      | GraphQL Java.                                                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| groovy-templates                     | Groovy templating engine.                                    |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| h2                                   | Provides a fast in-memory database that supports JDBC API    |                               |</span><br><span class="line">|                                      | and R2DBC access, with a small (2mb) footprint. Supports     |                               |</span><br><span class="line">|                                      | embedded and server modes as well as a browser based console |                               |</span><br><span class="line">|                                      | application.                                                 |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| hateoas                              | Eases the creation of RESTful APIs that follow the HATEOAS   |                               |</span><br><span class="line">|                                      | principle when working with Spring / Spring MVC.             |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| hsql                                 | Lightweight 100% Java SQL Database Engine.                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| influx                               | Publish Micrometer metrics to InfluxDB, a dimensional        |                               |</span><br><span class="line">|                                      | time-series server that support real-time stream processing  |                               |</span><br><span class="line">|                                      | of data.                                                     |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| integration                          | Adds support for Enterprise Integration Patterns. Enables    |                               |</span><br><span class="line">|                                      | lightweight messaging and supports integration with external |                               |</span><br><span class="line">|                                      | systems via declarative adapters.                            |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| jdbc                                 | Database Connectivity API that defines how a client may      |                               |</span><br><span class="line">|                                      | connect and query a database.                                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| jersey                               | Framework for developing RESTful Web Services in Java that   |                               |</span><br><span class="line">|                                      | provides support for JAX-RS APIs.                            |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| jooq                                 | Generate Java code from your database and build type safe    |                               |</span><br><span class="line">|                                      | SQL queries through a fluent API.                            |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| kafka                                | Publish, subscribe, store, and process streams of records.   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| kafka-streams                        | Building stream processing applications with Apache Kafka    |                               |</span><br><span class="line">|                                      | Streams.                                                     |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| liquibase                            | Liquibase database migration and source control library.     |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| lombok                               | Java annotation library which helps to reduce boilerplate    |                               |</span><br><span class="line">|                                      | code.                                                        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| mail                                 | Send email using Java Mail and Spring Framework&#x27;s            |                               |</span><br><span class="line">|                                      | JavaMailSender.                                              |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| mariadb                              | MariaDB JDBC and R2DBC driver.                               |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| mustache                             | Logic-less Templates. There are no if statements, else       |                               |</span><br><span class="line">|                                      | clauses, or for loops. Instead there are only tags.          |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| mybatis                              | Persistence framework with support for custom SQL, stored    | &gt;=2.0.0.RELEASE and &lt;3.1.0-M1 |</span><br><span class="line">|                                      | procedures and advanced mappings. MyBatis couples objects    |                               |</span><br><span class="line">|                                      | with stored procedures or SQL statements using a XML         |                               |</span><br><span class="line">|                                      | descriptor or annotations.                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| mysql                                | MySQL JDBC driver.                                           |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| native                               | Support for compiling Spring applications to native          | &gt;=3.0.0-M1                    |</span><br><span class="line">|                                      | executables using the GraalVM native-image compiler.         |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| new-relic                            | Publish Micrometer metrics to New Relic, a SaaS offering     |                               |</span><br><span class="line">|                                      | with a full UI and a query language called NRQL.             |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| oauth2-client                        | Spring Boot integration for Spring Security&#x27;s OAuth2/OpenID  |                               |</span><br><span class="line">|                                      | Connect client features.                                     |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| oauth2-resource-server               | Spring Boot integration for Spring Security&#x27;s OAuth2         | &gt;=2.1.0.M2                    |</span><br><span class="line">|                                      | resource server features.                                    |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| okta                                 | Okta specific configuration for Spring Security/Spring Boot  | &gt;=2.0.0.RELEASE and &lt;3.1.0-M1 |</span><br><span class="line">|                                      | OAuth2 features. Enable your Spring Boot application to work |                               |</span><br><span class="line">|                                      | with Okta via OAuth 2.0/OIDC.                                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| open-service-broker                  | Framework for building Spring Boot apps that implement the   | &gt;=2.0.0.RELEASE and &lt;2.7.0-M1 |</span><br><span class="line">|                                      | Open Service Broker API, which can deliver services to       |                               |</span><br><span class="line">|                                      | applications running within cloud native platforms such as   |                               |</span><br><span class="line">|                                      | Cloud Foundry, Kubernetes and OpenShift.                     |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| oracle                               | A JDBC driver that provides access to Oracle.                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| picocli                              | Build command line applications with picocli.                | &gt;=2.5.0.RELEASE and &lt;3.1.0-M1 |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| postgresql                           | A JDBC and R2DBC driver that allows Java programs to connect |                               |</span><br><span class="line">|                                      | to a PostgreSQL database using standard, database            |                               |</span><br><span class="line">|                                      | independent Java code.                                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| prometheus                           | Expose Micrometer metrics in Prometheus format, an in-memory |                               |</span><br><span class="line">|                                      | dimensional time series database with a simple built-in UI,  |                               |</span><br><span class="line">|                                      | a custom query language, and math operations.                |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| pulsar                               | Build messaging applications with Apache Pulsar              | &gt;=3.0.0 and &lt;3.1.0-M1         |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| pulsar-reactive                      | Build reactive messaging applications with Apache Pulsar     | &gt;=3.0.0 and &lt;3.1.0-M1         |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| quartz                               | Schedule jobs using Quartz.                                  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| restdocs                             | Document RESTful services by combining hand-written with     |                               |</span><br><span class="line">|                                      | Asciidoctor and auto-generated snippets produced with Spring |                               |</span><br><span class="line">|                                      | MVC Test.                                                    |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| rsocket                              | RSocket.io applications with Spring Messaging and Netty.     | &gt;=2.2.0.M2                    |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| scs-config-client                    | Config client on VMware Tanzu Application Service.           | &gt;=2.0.0.RELEASE and &lt;3.0.0-M1 |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| scs-service-registry                 | Eureka service discovery client on VMware Tanzu Application  | &gt;=2.0.0.RELEASE and &lt;3.0.0-M1 |</span><br><span class="line">|                                      | Service.                                                     |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| security                             | Highly customizable authentication and access-control        |                               |</span><br><span class="line">|                                      | framework for Spring applications.                           |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| session                              | Provides an API and implementations for managing user        |                               |</span><br><span class="line">|                                      | session information.                                         |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| solace                               | Connect to a Solace PubSub+ Advanced Event Broker to         | &gt;=2.2.0.RELEASE and &lt;3.0.0-M1 |</span><br><span class="line">|                                      | publish, subscribe, request/reply and store/replay messages  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| spring-shell                         | Build command line applications with spring.                 | &gt;=2.7.0 and &lt;3.1.0-M1         |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| sqlserver                            | A JDBC and R2DBC driver that provides access to Microsoft    |                               |</span><br><span class="line">|                                      | SQL Server and Azure SQL Database from any Java application. |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| testcontainers                       | Provide lightweight, throwaway instances of common           |                               |</span><br><span class="line">|                                      | databases, Selenium web browsers, or anything else that can  |                               |</span><br><span class="line">|                                      | run in a Docker container.                                   |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| thymeleaf                            | A modern server-side Java template engine for both web and   |                               |</span><br><span class="line">|                                      | standalone environments. Allows HTML to be correctly         |                               |</span><br><span class="line">|                                      | displayed in browsers and as static prototypes.              |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| unboundid-ldap                       | Provides a platform neutral way for running a LDAP server in |                               |</span><br><span class="line">|                                      | unit tests.                                                  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| vaadin                               | A web framework that allows you to write UI in pure Java     | &gt;=2.0.0.RELEASE and &lt;3.1.0-M1 |</span><br><span class="line">|                                      | without getting bogged down in JS, HTML, and CSS.            |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| validation                           | Bean Validation with Hibernate validator.                    |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| wavefront                            | Publish metrics and optionally distributed traces to Tanzu   |                               |</span><br><span class="line">|                                      | Observability by Wavefront, a SaaS-based metrics monitoring  |                               |</span><br><span class="line">|                                      | and analytics platform that lets you visualize, query, and   |                               |</span><br><span class="line">|                                      | alert over data from across your entire stack.               |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| web                                  | Build web, including RESTful, applications using Spring MVC. |                               |</span><br><span class="line">|                                      | Uses Apache Tomcat as the default embedded container.        |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| web-services                         | Facilitates contract-first SOAP development. Allows for the  |                               |</span><br><span class="line">|                                      | creation of flexible web services using one of the many ways |                               |</span><br><span class="line">|                                      | to manipulate XML payloads.                                  |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| webflux                              | Build reactive web applications with Spring WebFlux and      |                               |</span><br><span class="line">|                                      | Netty.                                                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| websocket                            | Build Servlet-based WebSocket applications with SockJS and   |                               |</span><br><span class="line">|                                      | STOMP.                                                       |                               |</span><br><span class="line">|                                      |                                                              |                               |</span><br><span class="line">| zipkin                               | Enable and expose span and trace IDs to Zipkin.              |                               |</span><br><span class="line">+--------------------------------------+--------------------------------------------------------------+-------------------------------+</span><br><span class="line"></span><br><span class="line">Examples:</span><br><span class="line"></span><br><span class="line">To create a default demo.zip:</span><br><span class="line">        $ curl -G https://start.spring.io/starter.zip -o demo.zip</span><br><span class="line"></span><br><span class="line">To create a web project using Java 11:</span><br><span class="line">        $ curl -G https://start.spring.io/starter.zip -d dependencies=web \</span><br><span class="line">                        -d javaVersion=11 -o demo.zip</span><br><span class="line"></span><br><span class="line">To create a web/data-jpa gradle project unpacked:</span><br><span class="line">        $ curl -G https://start.spring.io/starter.tgz -d dependencies=web,data-jpa \</span><br><span class="line">                   -d type=gradle-project -d baseDir=my-dir | tar -xzvf -</span><br><span class="line"></span><br><span class="line">To generate a Maven POM with war packaging:</span><br><span class="line">        $ curl -G https://start.spring.io/pom.xml -d packaging=war -o pom.xml</span><br></pre></td></tr></table></figure>

<h1 id="Boot-War项目"><a href="#Boot-War项目" class="headerlink" title="Boot War项目"></a>Boot War项目</h1><h2 id="创建War项目"><a href="#创建War项目" class="headerlink" title="创建War项目"></a>创建War项目</h2><p>创建一个SpringBoot War项目，点击文件-&gt;新建-&gt;项目，注意选择<code>War</code>打包方式</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/06.png" alt="img"></p>
<p>点击下一步，添加<code>Spring Web</code>依赖</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/07.png" alt="img"></p>
<p>点击创建。</p>
<p>如果想使用<code>jsp</code>技术一般以<code>war</code>方式打包项目，因为<code>jsp</code>是不能配合<code>jar</code>方式打包使用的。</p>
<p><code>jsp</code>视图放置的位置是固定的，必须在<code>main</code>文件夹下的<code>webapp</code>目录下，例如：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/08.png" alt="img"></p>
<p><code>hello.jsp</code>内容：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">&lt;%--</span><br><span class="line">  Created by IntelliJ IDEA.</span><br><span class="line">  User: WolfMan</span><br><span class="line">  Date: 2023/4/9</span><br><span class="line">  Time: 21:05</span><br><span class="line">  To change this template use File | Settings | File Templates.</span><br><span class="line">--%&gt;</span><br><span class="line">&lt;%@ page contentType=&quot;text/html;charset=UTF-8&quot; language=&quot;java&quot; %&gt;</span><br><span class="line"><span class="tag">&lt;<span class="name">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span>Title<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>Hello, World<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>我们创建一个<code>Controller</code>进行视图映射：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloController</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequestMapping(&quot;/hello&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">hello</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;hello&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>同时在<code>application.properties</code>文件中添加视图配置：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">spring.mvc.view.prefix=/</span><br><span class="line">spring.mvc.view.suffix=.jsp</span><br></pre></td></tr></table></figure>

<p>这样一个简单的<code>war</code>项目就已经编写完毕</p>
<h2 id="测试War项目"><a href="#测试War项目" class="headerlink" title="测试War项目"></a>测试War项目</h2><h3 id="使用外置tomcat服务器"><a href="#使用外置tomcat服务器" class="headerlink" title="使用外置tomcat服务器"></a>使用外置tomcat服务器</h3><p>配置外置<code>tomcat</code>服务器，点击编辑配置：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/09.png" alt="img"></p>
<p>点击+号，添加本地Tomcat服务器：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/10.png" alt="img"></p>
<p>点击修复：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/11.png" alt="img"></p>
<p>选择部署的工件，有两种选择：</p>
<ol>
<li><code>war</code>：打包成<code>war</code>包进行部署</li>
<li><code>war exploded</code>：不打成<code>war</code>包，只是将生成<code>war</code>包所在的目录进行部署</li>
</ol>
<p>测试一般选择第二种方式。</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/12.png" alt="img"></p>
<p>应用程序上下文一般为<code>/</code>：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/13.png" alt="img"></p>
<p>点击应用。</p>
<p>运行<code>tomcat</code>服务器，访问<a target="_blank" rel="noopener" href="http://localhost:8080/hello%E7%BD%91%E5%9D%80%EF%BC%8C%E6%B5%8F%E8%A7%88%E5%99%A8%E5%B1%95%E7%A4%BA%EF%BC%9A">http://localhost:8080/hello网址，浏览器展示：</a></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hello, World</span><br></pre></td></tr></table></figure>

<p>执行流程简单梳理：</p>
<ol>
<li>浏览器输入<a target="_blank" rel="noopener" href="http://localhost:8080/hello">http://localhost:8080/hello</a></li>
<li>经过<code>DispatcherServlet</code>，由<code>DispatcherServlet</code>调用<code>RequestMappingHandlerMapping</code>找到控制器方法</li>
<li>使用<code>RequestMappingHandlerAdapter</code>调用控制器方法</li>
<li>方法的返回值由返回值处理器进行解析，对于字符串返回值将会解析成视图名</li>
<li>由视图解析器拼接成完整视图路径</li>
<li>最后由<code>jsp</code>解析器解析并相应<code>html</code>代码</li>
</ol>
<p>值得注意的是，在骨架中生成了一个<code>ServletInitializer</code>类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ServletInitializer</span> <span class="keyword">extends</span> <span class="title class_">SpringBootServletInitializer</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> SpringApplicationBuilder <span class="title function_">configure</span><span class="params">(SpringApplicationBuilder application)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> application.sources(Test4Application.class);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个类就是配合外置的<code>tomcat</code>进行使用，相当于初始化类在<code>tomcat</code>启动的时候找到<code>SpringBoot</code>相关的代码并运行。如果缺少此类就没法和外置的<code>tomcat</code>配合使用。</p>
<h3 id="使用内置tomcat服务器"><a href="#使用内置tomcat服务器" class="headerlink" title="使用内置tomcat服务器"></a>使用内置tomcat服务器</h3><p>我们直接运行启动类：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">  .   ____          _            __ _ _</span><br><span class="line"> /\\ / ___&#x27;_ __ _ _(_)_ __  __ _ \ \ \ \</span><br><span class="line">( ( )\___ | &#x27;_ | &#x27;_| | &#x27;_ \/ _` | \ \ \ \</span><br><span class="line"> \\/  ___)| |_)| | | | | || (_| |  ) ) ) )</span><br><span class="line">  &#x27;  |____| .__|_| |_|_| |_\__, | / / / /</span><br><span class="line"> =========|_|==============|___/=/_/_/_/</span><br><span class="line"> :: Spring Boot ::                (v2.5.2)</span><br><span class="line"></span><br><span class="line">2023-04-09 21:35:29.072  INFO 19168 --- [           main] com.example.test4.Test4Application       : Starting Test4Application using Java 1.8.0_152 on LAPTOP-FBCMT03A with PID 19168 (F:\Java\java项目\Spring学习\springboot\test4\test4\target\classes started by WolfMan in F:\Java\java项目\Spring学习\springboot\test4\test4)</span><br><span class="line">2023-04-09 21:35:29.075  INFO 19168 --- [           main] com.example.test4.Test4Application       : No active profile set, falling back to default profiles: default</span><br><span class="line">2023-04-09 21:35:29.873  INFO 19168 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)</span><br><span class="line">2023-04-09 21:35:29.879  INFO 19168 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]</span><br><span class="line">2023-04-09 21:35:29.880  INFO 19168 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.48]</span><br><span class="line">2023-04-09 21:35:29.931  INFO 19168 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext</span><br><span class="line">2023-04-09 21:35:29.931  INFO 19168 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 805 ms</span><br><span class="line">2023-04-09 21:35:30.180  INFO 19168 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path &#x27;&#x27;</span><br><span class="line">2023-04-09 21:35:30.188  INFO 19168 --- [           main] com.example.test4.Test4Application       : Started Test4Application in 1.467 seconds (JVM running for 2.169)</span><br></pre></td></tr></table></figure>

<p>查看日志发现，服务已经启动并且监听了8080端口。</p>
<p>访问<a target="_blank" rel="noopener" href="http://localhost:8080/hello%EF%BC%8C%E6%88%91%E4%BB%AC%E5%8F%91%E7%8E%B0%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9B%B4%E6%8E%A5%E5%B0%86%60hello.jsp%60%E5%BD%93%E6%88%90%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD%E4%B8%8B%E6%9D%A5%E4%BA%86%EF%BC%8C%E5%B9%B6%E6%B2%A1%E6%9C%89%E8%A7%A3%E6%9E%90%E9%A1%B5%E9%9D%A2%E3%80%82%E8%BF%99%E6%98%AF%E5%9B%A0%E4%B8%BA%E5%86%85%E5%B5%8C%E7%9A%84%60tomcat%60%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%B2%A1%E6%9C%89%E8%87%AA%E5%B8%A6%60jsp%60%E7%9A%84%E8%A7%A3%E6%9E%90%E5%99%A8%E3%80%82">http://localhost:8080/hello，我们发现浏览器直接将`hello.jsp`当成文件下载下来了，并没有解析页面。这是因为内嵌的`tomcat`服务器没有自带`jsp`的解析器。</a></p>
<p>我们在<code>pom.xml</code>中添加<code>jsp</code>解析器坐标：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.apache.tomcat.embed<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>tomcat-embed-jasper<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>重新运行启动类，访问<a target="_blank" rel="noopener" href="http://localhost:8080/hello%E7%BD%91%E5%9D%80%EF%BC%8C%E6%B5%8F%E8%A7%88%E5%99%A8%E5%B1%95%E7%A4%BA%EF%BC%9A">http://localhost:8080/hello网址，浏览器展示：</a></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Hello, World</span><br></pre></td></tr></table></figure>

<h1 id="Boot启动流程"><a href="#Boot启动流程" class="headerlink" title="Boot启动流程"></a>Boot启动流程</h1><h2 id="SpringApplication构造分析"><a href="#SpringApplication构造分析" class="headerlink" title="SpringApplication构造分析"></a>SpringApplication构造分析</h2><p>创建启动类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        SpringApplication.run(A39_1.class,args);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们进入<code>SpringApplication</code>的<code>run()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ConfigurableApplicationContext <span class="title function_">run</span><span class="params">(Class&lt;?&gt; primarySource, String... args)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> run(<span class="keyword">new</span> <span class="title class_">Class</span>[]&#123;primarySource&#125;, args);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入重载的<code>run()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ConfigurableApplicationContext <span class="title function_">run</span><span class="params">(Class&lt;?&gt;[] primarySources, String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">new</span> <span class="title class_">SpringApplication</span>(primarySources)).run(args);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中创建了一个<code>SpringApplication</code>实例，并调用了实例的<code>run()</code>方法。</p>
<p>进入<code>SpringApplication</code>的构造方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">SpringApplication</span><span class="params">(Class&lt;?&gt;... primarySources)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>((ResourceLoader)<span class="literal">null</span>, primarySources);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入重载的构造方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">SpringApplication</span><span class="params">(ResourceLoader resourceLoader, Class&lt;?&gt;... primarySources)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.sources = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>();</span><br><span class="line">    <span class="built_in">this</span>.bannerMode = Mode.CONSOLE;</span><br><span class="line">    <span class="built_in">this</span>.logStartupInfo = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.addCommandLineProperties = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.addConversionService = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.headless = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.registerShutdownHook = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.additionalProfiles = Collections.emptySet();</span><br><span class="line">    <span class="built_in">this</span>.isCustomEnvironment = <span class="literal">false</span>;</span><br><span class="line">    <span class="built_in">this</span>.lazyInitialization = <span class="literal">false</span>;</span><br><span class="line">    <span class="built_in">this</span>.applicationContextFactory = ApplicationContextFactory.DEFAULT;</span><br><span class="line">    <span class="built_in">this</span>.applicationStartup = ApplicationStartup.DEFAULT;</span><br><span class="line">    <span class="built_in">this</span>.resourceLoader = resourceLoader;</span><br><span class="line">    Assert.notNull(primarySources, <span class="string">&quot;PrimarySources must not be null&quot;</span>);</span><br><span class="line">    <span class="built_in">this</span>.primarySources = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>(Arrays.asList(primarySources));</span><br><span class="line">    <span class="built_in">this</span>.webApplicationType = WebApplicationType.deduceFromClasspath();</span><br><span class="line">    <span class="built_in">this</span>.bootstrapRegistryInitializers = <span class="built_in">this</span>.getBootstrapRegistryInitializersFromSpringFactories();</span><br><span class="line">    <span class="built_in">this</span>.setInitializers(<span class="built_in">this</span>.getSpringFactoriesInstances(ApplicationContextInitializer.class));</span><br><span class="line">    <span class="built_in">this</span>.setListeners(<span class="built_in">this</span>.getSpringFactoriesInstances(ApplicationListener.class));</span><br><span class="line">    <span class="built_in">this</span>.mainApplicationClass = <span class="built_in">this</span>.deduceMainApplicationClass();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>SpringApplication</code>的构造方法主要做了以下五件事：</p>
<ol>
<li>记录<code>BeanDefinition</code>源，配置类或者<code>xml</code>配置文件等，根据引导类寻找<code>BeanDefinition</code>源</li>
<li>推断应用类型，非<code>web</code>程序、基于<code>servlet</code>的<code>web</code>程序、基于<code>reactive</code>的<code>web</code>容器，根据当前类路径下的<code>jar</code>包中的关键类推断</li>
<li>记录<code>ApplicationContext</code>初始化器，<code>ApplicationContext</code>初始化器可以对<code>Application</code>做一些功能上的扩展</li>
<li>记录监听器与事件，监听<code>SpringBoot</code>在启动过程中的一些关键事件</li>
<li>推断主启动类，推断运行<code>SpringBoot</code>项目的类</li>
</ol>
<p><code>SpringApplication</code>的构造方法主要是做一些准备工作，并没有真正创建<code>spring</code>容器，<code>spring</code>容器的创建是在<code>SpringApplication</code>的<code>run()</code>方法中。</p>
<ol>
<li>演示获取<code>BeanDefinition</code>源</li>
</ol>
<p>编写测试代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;1. 演示获取 Bean Definition 源&quot;</span>);</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">spring</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(A39_1.class);</span><br><span class="line">        spring.setSources(Set.of(<span class="string">&quot;classpath:b01.xml&quot;</span>));</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> spring.run(args);</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;name: &quot;</span> + name + <span class="string">&quot; 来源：&quot;</span> + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());</span><br><span class="line">        &#125;</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean3</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 需要TomcatServletWebServerFactory</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TomcatServletWebServerFactory <span class="title function_">servletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>编写配置文件：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bean1&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.itheima.a39.A39_1.Bean1&quot;</span>/&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">1. 演示获取 Bean Definition 源</span><br><span class="line">name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 来源：null</span><br><span class="line">name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 来源：null</span><br><span class="line">name: org.springframework.context.annotation.internalCommonAnnotationProcessor 来源：null</span><br><span class="line">name: org.springframework.context.event.internalEventListenerProcessor 来源：null</span><br><span class="line">name: org.springframework.context.event.internalEventListenerFactory 来源：null</span><br><span class="line">name: a39_1 来源：null</span><br><span class="line">name: bean1 来源：class path resource [b01.xml]</span><br><span class="line">name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源：null</span><br><span class="line">name: bean2 来源：com.itheima.a39.A39_1</span><br><span class="line">name: servletWebServerFactory 来源：com.itheima.a39.A39_1</span><br></pre></td></tr></table></figure>

<p>发现将我们自定义的<code>Bean</code>的来源都已打印，来源为<code>null</code>的是<code>spring</code>自己添加的<code>Bean</code>，并且我们设置的xml的配置文件也已经生效。</p>
<ol start="2">
<li>演示推断应用类型</li>
</ol>
<p>进入<code>SpringApplication</code>的构造方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">SpringApplication</span><span class="params">(ResourceLoader resourceLoader, Class&lt;?&gt;... primarySources)</span> &#123;</span><br><span class="line">    <span class="built_in">this</span>.sources = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>();</span><br><span class="line">    <span class="built_in">this</span>.bannerMode = Mode.CONSOLE;</span><br><span class="line">    <span class="built_in">this</span>.logStartupInfo = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.addCommandLineProperties = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.addConversionService = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.headless = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.registerShutdownHook = <span class="literal">true</span>;</span><br><span class="line">    <span class="built_in">this</span>.additionalProfiles = Collections.emptySet();</span><br><span class="line">    <span class="built_in">this</span>.isCustomEnvironment = <span class="literal">false</span>;</span><br><span class="line">    <span class="built_in">this</span>.lazyInitialization = <span class="literal">false</span>;</span><br><span class="line">    <span class="built_in">this</span>.applicationContextFactory = ApplicationContextFactory.DEFAULT;</span><br><span class="line">    <span class="built_in">this</span>.applicationStartup = ApplicationStartup.DEFAULT;</span><br><span class="line">    <span class="built_in">this</span>.resourceLoader = resourceLoader;</span><br><span class="line">    Assert.notNull(primarySources, <span class="string">&quot;PrimarySources must not be null&quot;</span>);</span><br><span class="line">    <span class="built_in">this</span>.primarySources = <span class="keyword">new</span> <span class="title class_">LinkedHashSet</span>(Arrays.asList(primarySources));</span><br><span class="line">    <span class="built_in">this</span>.webApplicationType = WebApplicationType.deduceFromClasspath();</span><br><span class="line">    <span class="built_in">this</span>.bootstrapRegistryInitializers = <span class="built_in">this</span>.getBootstrapRegistryInitializersFromSpringFactories();</span><br><span class="line">    <span class="built_in">this</span>.setInitializers(<span class="built_in">this</span>.getSpringFactoriesInstances(ApplicationContextInitializer.class));</span><br><span class="line">    <span class="built_in">this</span>.setListeners(<span class="built_in">this</span>.getSpringFactoriesInstances(ApplicationListener.class));</span><br><span class="line">    <span class="built_in">this</span>.mainApplicationClass = <span class="built_in">this</span>.deduceMainApplicationClass();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>推断类型的逻辑在<code>WebApplicationType</code>的<code>deduceFromClasspath()</code>方法中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> WebApplicationType <span class="title function_">deduceFromClasspath</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (ClassUtils.isPresent(<span class="string">&quot;org.springframework.web.reactive.DispatcherHandler&quot;</span>, (ClassLoader)<span class="literal">null</span>) &amp;&amp; !ClassUtils.isPresent(<span class="string">&quot;org.springframework.web.servlet.DispatcherServlet&quot;</span>, (ClassLoader)<span class="literal">null</span>) &amp;&amp; !ClassUtils.isPresent(<span class="string">&quot;org.glassfish.jersey.servlet.ServletContainer&quot;</span>, (ClassLoader)<span class="literal">null</span>)) &#123;</span><br><span class="line">        <span class="keyword">return</span> REACTIVE;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        String[] var0 = SERVLET_INDICATOR_CLASSES;</span><br><span class="line">        <span class="type">int</span> <span class="variable">var1</span> <span class="operator">=</span> var0.length;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">var2</span> <span class="operator">=</span> <span class="number">0</span>; var2 &lt; var1; ++var2) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">className</span> <span class="operator">=</span> var0[var2];</span><br><span class="line">            <span class="keyword">if</span> (!ClassUtils.isPresent(className, (ClassLoader)<span class="literal">null</span>)) &#123;</span><br><span class="line">                <span class="keyword">return</span> NONE;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> SERVLET;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>主要判断逻辑为类路径下是否存在某些类，或者不存在某些类。</p>
<p>我们通过反射来调用<code>WebApplicationType</code>的<code>deduceFromClasspath()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">spring</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(A39_1.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;2. 演示推断应用类型&quot;</span>);</span><br><span class="line">        <span class="type">Method</span> <span class="variable">deduceFromClasspath</span> <span class="operator">=</span> WebApplicationType.class.getDeclaredMethod(<span class="string">&quot;deduceFromClasspath&quot;</span>);</span><br><span class="line">        deduceFromClasspath.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;应用类型为:&quot;</span>+deduceFromClasspath.invoke(<span class="literal">null</span>));</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> spring.run(args);</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean3</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TomcatServletWebServerFactory <span class="title function_">servletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">2. 演示推断应用类型</span><br><span class="line">应用类型为:SERVLET</span><br></pre></td></tr></table></figure>

<ol start="3">
<li>演示<code>ApplicationContext</code>初始化器</li>
</ol>
<p>查看<code>ApplicationContext</code>构造方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="built_in">this</span>.setInitializers(<span class="built_in">this</span>.getSpringFactoriesInstances(ApplicationContextInitializer.class));</span><br><span class="line"><span class="comment">//...</span></span><br></pre></td></tr></table></figure>

<p>以上代码就是读取配置文件中的初始化器。</p>
<p>编写测试代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">spring</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(A39_1.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;3. 演示 ApplicationContext 初始化器&quot;</span>);</span><br><span class="line">        spring.addInitializers(applicationContext -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (applicationContext <span class="keyword">instanceof</span> GenericApplicationContext gac) &#123;</span><br><span class="line">                gac.registerBean(<span class="string">&quot;bean3&quot;</span>, Bean3.class);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> spring.run(args);</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;name: &quot;</span> + name + <span class="string">&quot; 来源：&quot;</span> + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());</span><br><span class="line">        &#125;</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean3</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TomcatServletWebServerFactory <span class="title function_">servletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">3. 演示 ApplicationContext 初始化器</span><br><span class="line">name: org.springframework.context.annotation.internalConfigurationAnnotationProcessor 来源：null</span><br><span class="line">name: org.springframework.context.annotation.internalAutowiredAnnotationProcessor 来源：null</span><br><span class="line">name: org.springframework.context.annotation.internalCommonAnnotationProcessor 来源：null</span><br><span class="line">name: org.springframework.context.event.internalEventListenerProcessor 来源：null</span><br><span class="line">name: org.springframework.context.event.internalEventListenerFactory 来源：null</span><br><span class="line">name: bean3 来源：null</span><br><span class="line">name: a39_1 来源：null</span><br><span class="line">name: org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源：null</span><br><span class="line">name: bean2 来源：com.itheima.a39.A39_1</span><br><span class="line">name: servletWebServerFactory 来源：com.itheima.a39.A39_1</span><br></pre></td></tr></table></figure>

<p>我们发现<code>bean3</code>已经注册在容器中。</p>
<ol start="4">
<li>演示监听器与事件</li>
</ol>
<p>查看<code>ApplicationContext</code>构造方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="built_in">this</span>.setListeners(<span class="built_in">this</span>.getSpringFactoriesInstances(ApplicationListener.class));</span><br><span class="line"><span class="comment">//...</span></span><br></pre></td></tr></table></figure>

<p>以上代码就是读取配置文件中的监听器。</p>
<p>编写测试代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">spring</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(A39_1.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;4. 演示监听器与事件&quot;</span>);</span><br><span class="line">        spring.addListeners(event -&gt; System.out.println(<span class="string">&quot;\t事件为:&quot;</span> + event.getClass()));</span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> spring.run(args);</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;name: &quot;</span> + name + <span class="string">&quot; 来源：&quot;</span> + context.getBeanFactory().getBeanDefinition(name).getResourceDescription());</span><br><span class="line">        &#125;</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean3</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TomcatServletWebServerFactory <span class="title function_">servletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">4. 演示监听器与事件</span><br><span class="line">        事件为:class org.springframework.boot.context.event.ApplicationStartingEvent</span><br><span class="line">        事件为:class org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent</span><br><span class="line"></span><br><span class="line">  .   ____          _            __ _ _</span><br><span class="line"> /\\ / ___&#x27;_ __ _ _(_)_ __  __ _ \ \ \ \</span><br><span class="line">( ( )\___ | &#x27;_ | &#x27;_| | &#x27;_ \/ _` | \ \ \ \</span><br><span class="line"> \\/  ___)| |_)| | | | | || (_| |  ) ) ) )</span><br><span class="line">  &#x27;  |____| .__|_| |_|_| |_\__, | / / / /</span><br><span class="line"> =========|_|==============|___/=/_/_/_/</span><br><span class="line"> :: Spring Boot ::                (v2.5.5)</span><br><span class="line"></span><br><span class="line">        事件为:class org.springframework.boot.context.event.ApplicationContextInitializedEvent</span><br><span class="line">[INFO ] 13:58:33.919 [main] com.itheima.a39.A39_1               - Starting A39_1 using Java 17.0.6 on LAPTOP-FBCMT03A with PID 29512 (F:\Java\黑马全套java教程\第2阶段企业级开发—基础框架\7、spring高级45讲\代码\代码\show\target\classes started by WolfMan in F:\Java\黑马全套java教程\第2阶段企业级开发—基础框架\7、spring高级45讲\代码\代码) </span><br><span class="line">[DEBUG] 13:58:33.926 [main] com.itheima.a39.A39_1               - Running with Spring Boot v2.5.5, Spring v5.3.10 </span><br><span class="line">[INFO ] 13:58:33.927 [main] com.itheima.a39.A39_1               - No active profile set, falling back to default profiles: default </span><br><span class="line">        事件为:class org.springframework.boot.context.event.ApplicationPreparedEvent</span><br><span class="line">[INFO ] 13:58:34.398 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat initialized with port(s): 8080 (http) </span><br><span class="line">[INFO ] 13:58:34.413 [main] o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler [&quot;http-nio-8080&quot;] </span><br><span class="line">[INFO ] 13:58:34.414 [main] o.a.catalina.core.StandardService   - Starting service [Tomcat] </span><br><span class="line">[INFO ] 13:58:34.414 [main] o.a.catalina.core.StandardEngine    - Starting Servlet engine: [Apache Tomcat/9.0.53] </span><br><span class="line">[INFO ] 13:58:34.560 [main] o.a.c.c.C.[Tomcat].[localhost].[/]  - Initializing Spring embedded WebApplicationContext </span><br><span class="line">[INFO ] 13:58:34.560 [main] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 597 ms </span><br><span class="line">[INFO ] 13:58:34.617 [main] o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler [&quot;http-nio-8080&quot;] </span><br><span class="line">[INFO ] 13:58:34.670 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat started on port(s): 8080 (http) with context path &#x27;&#x27; </span><br><span class="line">        事件为:class org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent</span><br><span class="line">        事件为:class org.springframework.context.event.ContextRefreshedEvent</span><br><span class="line">[INFO ] 13:58:34.678 [main] com.itheima.a39.A39_1               - Started A39_1 in 1.214 seconds (JVM running for 1.692) </span><br><span class="line">        事件为:class org.springframework.boot.context.event.ApplicationStartedEvent</span><br><span class="line">        事件为:class org.springframework.boot.availability.AvailabilityChangeEvent</span><br><span class="line">        事件为:class org.springframework.boot.context.event.ApplicationReadyEvent</span><br><span class="line">        事件为:class org.springframework.boot.availability.AvailabilityChangeEvent</span><br><span class="line">        事件为:class org.springframework.boot.availability.AvailabilityChangeEvent</span><br><span class="line">        事件为:class org.springframework.context.event.ContextClosedEvent</span><br></pre></td></tr></table></figure>

<p>事件被触发时会打印相应的事件名。</p>
<ol start="5">
<li>演示主类推断</li>
</ol>
<p>查看<code>ApplicationContext</code>构造方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//...</span></span><br><span class="line"><span class="built_in">this</span>.mainApplicationClass = <span class="built_in">this</span>.deduceMainApplicationClass();</span><br><span class="line"><span class="comment">//...</span></span><br></pre></td></tr></table></figure>

<p>以上代码就是推断主类方法，我们反射调用。</p>
<p>编写测试代码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">spring</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>(A39_1.class);</span><br><span class="line">        System.out.println(<span class="string">&quot;5. 演示主类推断&quot;</span>);</span><br><span class="line">        <span class="type">Method</span> <span class="variable">deduceMainApplicationClass</span> <span class="operator">=</span> SpringApplication.class.getDeclaredMethod(<span class="string">&quot;deduceMainApplicationClass&quot;</span>);</span><br><span class="line">        deduceMainApplicationClass.setAccessible(<span class="literal">true</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;\t主类是：&quot;</span>+deduceMainApplicationClass.invoke(spring));</span><br><span class="line"></span><br><span class="line">        <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> spring.run(args);</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean3</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> TomcatServletWebServerFactory <span class="title function_">servletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">5. 演示主类推断</span><br><span class="line">        主类是：class com.itheima.a39.A39_1</span><br></pre></td></tr></table></figure>

<h2 id="SpringApplication-run-方法分析"><a href="#SpringApplication-run-方法分析" class="headerlink" title="SpringApplication run()方法分析"></a>SpringApplication run()方法分析</h2><p>进入<code>SpringApplication</code>的<code>run()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> ConfigurableApplicationContext <span class="title function_">run</span><span class="params">(String... args)</span> &#123;</span><br><span class="line">    <span class="type">StopWatch</span> <span class="variable">stopWatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StopWatch</span>();</span><br><span class="line">    stopWatch.start();</span><br><span class="line">    <span class="type">DefaultBootstrapContext</span> <span class="variable">bootstrapContext</span> <span class="operator">=</span> <span class="built_in">this</span>.createBootstrapContext();</span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="built_in">this</span>.configureHeadlessProperty();</span><br><span class="line">    <span class="type">SpringApplicationRunListeners</span> <span class="variable">listeners</span> <span class="operator">=</span> <span class="built_in">this</span>.getRunListeners(args);</span><br><span class="line">    listeners.starting(bootstrapContext, <span class="built_in">this</span>.mainApplicationClass);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">ApplicationArguments</span> <span class="variable">applicationArguments</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultApplicationArguments</span>(args);</span><br><span class="line">        <span class="type">ConfigurableEnvironment</span> <span class="variable">environment</span> <span class="operator">=</span> <span class="built_in">this</span>.prepareEnvironment(listeners, bootstrapContext, applicationArguments);</span><br><span class="line">        <span class="built_in">this</span>.configureIgnoreBeanInfo(environment);</span><br><span class="line">        <span class="type">Banner</span> <span class="variable">printedBanner</span> <span class="operator">=</span> <span class="built_in">this</span>.printBanner(environment);</span><br><span class="line">        context = <span class="built_in">this</span>.createApplicationContext();</span><br><span class="line">        context.setApplicationStartup(<span class="built_in">this</span>.applicationStartup);</span><br><span class="line">        <span class="built_in">this</span>.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);</span><br><span class="line">        <span class="built_in">this</span>.refreshContext(context);</span><br><span class="line">        <span class="built_in">this</span>.afterRefresh(context, applicationArguments);</span><br><span class="line">        stopWatch.stop();</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.logStartupInfo) &#123;</span><br><span class="line">            (<span class="keyword">new</span> <span class="title class_">StartupInfoLogger</span>(<span class="built_in">this</span>.mainApplicationClass)).logStarted(<span class="built_in">this</span>.getApplicationLog(), stopWatch);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        listeners.started(context);</span><br><span class="line">        <span class="built_in">this</span>.callRunners(context, applicationArguments);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable var10) &#123;</span><br><span class="line">        <span class="built_in">this</span>.handleRunFailure(context, var10, listeners);</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(var10);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        listeners.running(context);</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable var9) &#123;</span><br><span class="line">        <span class="built_in">this</span>.handleRunFailure(context, var9, (SpringApplicationRunListeners)<span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(var9);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>SpringApplication</code>的<code>run()</code>方法步骤：</p>
<ol>
<li>得到<code>SpringApplicationRunListeners</code>，名字取得不好，实际是事件发布器<ol>
<li>发布<code>application starting</code>事件</li>
</ol>
</li>
<li>封装启动<code>args</code></li>
<li>准备<code>Environment</code>添加命令行参数</li>
<li><code>ConfigurationPropertySources</code>处理<ol>
<li>发布<code>application environment</code>已准备事件</li>
</ol>
</li>
<li>通过<code>EnvironmentPostProcessorApplicationListener</code>进行 <code>env </code>后处理<ol>
<li><code>application.properties</code>，由<code>StandardConfigDataLocationResolver</code>解析</li>
<li><code>spring.application.json</code></li>
</ol>
</li>
<li>绑定<code>spring.main</code>到<code>SpringApplication</code>对象</li>
<li>打印<code>banner</code></li>
<li>创建容器</li>
<li>准备容器<ol>
<li>发布<code>application context</code>已初始化事件</li>
</ol>
</li>
<li>加载<code>bean</code>定义<ol>
<li>发布<code>application prepared</code>事件</li>
</ol>
</li>
<li><code>refresh</code>容器<ol>
<li>发布<code>application started</code>事件</li>
</ol>
</li>
<li>执行<code>runner</code><ol>
<li>发布<code>application ready</code>事件</li>
<li>这其中有异常，发布<code>application failed</code>事件</li>
</ol>
</li>
</ol>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第1步：</p>
<p>事件发布器的类型为<code>SpringApplicationRunListener</code>，<code>SpringApplicationRunListeners</code>可以组合多个事件发布器。<code>SpringApplicationRunListener</code>有一个实现为<code>EventPublishingRunListener</code>，虽然只有一个实现，但<code>spring</code>没有写死在代码中，而是将接口与实现写在了<code>org.springframework.boot:spring-boot:2.5.5</code>包下的<code>spring.factories</code>配置文件中。</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Run Listeners</span></span><br><span class="line"><span class="attr">org.springframework.boot.SpringApplicationRunListener</span>=<span class="string">\</span></span><br><span class="line"><span class="string">org.springframework.boot.context.event.EventPublishingRunListener</span></span><br></pre></td></tr></table></figure>

<p>编写代码查看<code>EventPublishingRunListener</code>实现类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_2</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception&#123;</span><br><span class="line">        <span class="comment">// 添加 app 监听器</span></span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        app.addListeners(e -&gt; System.out.println(e.getClass()));</span><br><span class="line">        <span class="comment">// 获取事件发送器实现类名</span></span><br><span class="line">        List&lt;String&gt; names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());</span><br><span class="line">        <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.context.event.EventPublishingRunListener</span><br></pre></td></tr></table></figure>

<p>我们使用<code>EventPublishingRunListener</code>来发布各种事件：</p>
<ol>
<li><code>class org.springframework.boot.context.event.ApplicationStartingEvent</code></li>
<li><code>class org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent</code></li>
<li><code>class org.springframework.boot.context.event.ApplicationContextInitializedEvent</code></li>
<li><code>class org.springframework.boot.context.event.ApplicationPreparedEvent</code></li>
<li><code>class org.springframework.boot.context.event.ApplicationStartedEvent</code></li>
<li><code>class org.springframework.boot.context.event.ApplicationReadyEvent</code></li>
<li><code>class org.springframework.boot.context.event.ApplicationFailedEvent</code></li>
</ol>
<p>一共有以上七个事件。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_2</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="comment">// 添加 app 监听器</span></span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        app.addListeners(e -&gt; System.out.println(e.getClass()));</span><br><span class="line">        <span class="comment">// 获取事件发送器实现类名</span></span><br><span class="line">        List&lt;String&gt; names = SpringFactoriesLoader.loadFactoryNames(SpringApplicationRunListener.class, A39_2.class.getClassLoader());</span><br><span class="line">        <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">            Class&lt;?&gt; clazz = Class.forName(name);</span><br><span class="line">            Constructor&lt;?&gt; constructor = clazz.getConstructor(SpringApplication.class, String[].class);</span><br><span class="line">            <span class="type">SpringApplicationRunListener</span> <span class="variable">publisher</span> <span class="operator">=</span> (SpringApplicationRunListener) constructor.newInstance(app, args);</span><br><span class="line">            <span class="comment">// 发布事件</span></span><br><span class="line">            <span class="type">DefaultBootstrapContext</span> <span class="variable">bootstrapContext</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultBootstrapContext</span>();</span><br><span class="line">            <span class="comment">// spring boot 开始启动</span></span><br><span class="line">            publisher.starting(bootstrapContext);</span><br><span class="line">            <span class="comment">// 环境信息准备完毕</span></span><br><span class="line">            publisher.environmentPrepared(bootstrapContext, <span class="keyword">new</span> <span class="title class_">StandardEnvironment</span>());</span><br><span class="line">            <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericApplicationContext</span>();</span><br><span class="line">            <span class="comment">// 在 spring 容器创建，并调用初始化器之后，发送此事件</span></span><br><span class="line">            publisher.contextPrepared(context);</span><br><span class="line">            <span class="comment">// 所有 bean definition 加载完毕</span></span><br><span class="line">            publisher.contextLoaded(context);</span><br><span class="line">            context.refresh();</span><br><span class="line">            <span class="comment">// spring 容器初始化完成(refresh 方法调用完毕)</span></span><br><span class="line">            publisher.started(context);</span><br><span class="line">            <span class="comment">// spring boot 启动完毕</span></span><br><span class="line">            publisher.running(context);</span><br><span class="line">            <span class="comment">// spring boot 启动出错</span></span><br><span class="line">            publisher.failed(context, <span class="keyword">new</span> <span class="title class_">Exception</span>(<span class="string">&quot;出错了&quot;</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.context.event.EventPublishingRunListener</span><br><span class="line">class org.springframework.boot.context.event.ApplicationStartingEvent</span><br><span class="line">class org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent</span><br><span class="line">class org.springframework.boot.context.event.ApplicationContextInitializedEvent</span><br><span class="line">class org.springframework.boot.context.event.ApplicationPreparedEvent</span><br><span class="line">class org.springframework.context.event.ContextRefreshedEvent</span><br><span class="line">class org.springframework.boot.context.event.ApplicationStartedEvent</span><br><span class="line">class org.springframework.boot.availability.AvailabilityChangeEvent</span><br><span class="line">class org.springframework.boot.context.event.ApplicationReadyEvent</span><br><span class="line">class org.springframework.boot.availability.AvailabilityChangeEvent</span><br><span class="line">class org.springframework.boot.context.event.ApplicationFailedEvent</span><br></pre></td></tr></table></figure>

<p>发现控制台不止七个事件，其实有些事件是由容器内的事件发布器发布的。</p>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第2、8~12步：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 运行时请添加运行参数 --server.port=8080 debug</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A39_3</span> &#123;</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        app.addInitializers(<span class="keyword">new</span> <span class="title class_">ApplicationContextInitializer</span>&lt;ConfigurableApplicationContext&gt;() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">initialize</span><span class="params">(ConfigurableApplicationContext applicationContext)</span> &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;执行初始化器增强...&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 2. 封装启动 args&quot;</span>);</span><br><span class="line">        <span class="type">DefaultApplicationArguments</span> <span class="variable">arguments</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultApplicationArguments</span>(args);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 8. 创建容器&quot;</span>);</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> createApplicationContext(WebApplicationType.SERVLET);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 9. 准备容器&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (ApplicationContextInitializer initializer : app.getInitializers()) &#123;</span><br><span class="line">            initializer.initialize(context);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 10. 加载 bean 定义&quot;</span>);</span><br><span class="line">        <span class="type">DefaultListableBeanFactory</span> <span class="variable">beanFactory</span> <span class="operator">=</span> context.getDefaultListableBeanFactory();</span><br><span class="line">        <span class="type">AnnotatedBeanDefinitionReader</span> <span class="variable">reader1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotatedBeanDefinitionReader</span>(beanFactory);</span><br><span class="line">        <span class="type">XmlBeanDefinitionReader</span> <span class="variable">reader2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">XmlBeanDefinitionReader</span>(beanFactory);</span><br><span class="line">        <span class="type">ClassPathBeanDefinitionScanner</span> <span class="variable">scanner</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ClassPathBeanDefinitionScanner</span>(beanFactory);</span><br><span class="line">        reader1.register(Config.class);</span><br><span class="line">        reader2.loadBeanDefinitions(<span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;b03.xml&quot;</span>));</span><br><span class="line">        scanner.scan(<span class="string">&quot;com.itheima.a39.sub&quot;</span>);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 11. refresh 容器&quot;</span>);</span><br><span class="line">        context.refresh();</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;name:&quot;</span> + name + <span class="string">&quot; 来源：&quot;</span> + beanFactory.getBeanDefinition(name).getResourceDescription());</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 12. 执行 runner&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (CommandLineRunner runner : context.getBeansOfType(CommandLineRunner.class).values()) &#123;</span><br><span class="line">            runner.run(args);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (ApplicationRunner runner : context.getBeansOfType(ApplicationRunner.class).values()) &#123;</span><br><span class="line">            runner.run(arguments);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> GenericApplicationContext <span class="title function_">createApplicationContext</span><span class="params">(WebApplicationType type)</span> &#123;</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">switch</span> (type) &#123;</span><br><span class="line">            <span class="keyword">case</span> SERVLET -&gt; context = <span class="keyword">new</span> <span class="title class_">AnnotationConfigServletWebServerApplicationContext</span>();</span><br><span class="line">            <span class="keyword">case</span> REACTIVE -&gt; context = <span class="keyword">new</span> <span class="title class_">AnnotationConfigReactiveWebServerApplicationContext</span>();</span><br><span class="line">            <span class="keyword">case</span> NONE -&gt; context = <span class="keyword">new</span> <span class="title class_">AnnotationConfigApplicationContext</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean4</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean5</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean6</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean5 <span class="title function_">bean5</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean5</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> ServletWebServerFactory <span class="title function_">servletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> CommandLineRunner <span class="title function_">commandLineRunner</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">CommandLineRunner</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">(String... args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;commandLineRunner()...&quot;</span> + Arrays.toString(args));</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> ApplicationRunner <span class="title function_">applicationRunner</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ApplicationRunner</span>() &#123;</span><br><span class="line">                <span class="meta">@Override</span></span><br><span class="line">                <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">(ApplicationArguments args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;applicationRunner()...&quot;</span> + Arrays.toString(args.getSourceArgs()));</span><br><span class="line">                    System.out.println(args.getOptionNames());</span><br><span class="line">                    System.out.println(args.getOptionValues(<span class="string">&quot;server.port&quot;</span>));</span><br><span class="line">                    System.out.println(args.getNonOptionArgs());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>b03.xml</code>文件内容为：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://www.springframework.org/schema/beans&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xmlns:context</span>=<span class="string">&quot;http://www.springframework.org/schema/context&quot;</span></span></span><br><span class="line"><span class="tag">       <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line">    <span class="tag">&lt;<span class="name">bean</span> <span class="attr">id</span>=<span class="string">&quot;bean4&quot;</span> <span class="attr">class</span>=<span class="string">&quot;com.itheima.a39.A39_3.Bean4&quot;</span>/&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">beans</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p><code>com.itheima.a39.sub</code>包中内容为：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Bean7</span> &#123;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 2. 封装启动 args</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 8. 创建容器</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 9. 准备容器</span><br><span class="line">执行初始化器增强...</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 10. 加载 bean 定义</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 11. refresh 容器</span><br><span class="line">[INFO ] 16:05:35.122 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat initialized with port(s): 8080 (http) </span><br><span class="line">4月 12, 2023 4:05:35 下午 org.apache.coyote.AbstractProtocol init</span><br><span class="line">信息: Initializing ProtocolHandler [&quot;http-nio-8080&quot;]</span><br><span class="line">4月 12, 2023 4:05:35 下午 org.apache.catalina.core.StandardService startInternal</span><br><span class="line">信息: Starting service [Tomcat]</span><br><span class="line">4月 12, 2023 4:05:35 下午 org.apache.catalina.core.StandardEngine startInternal</span><br><span class="line">信息: Starting Servlet engine: [Apache Tomcat/9.0.53]</span><br><span class="line">4月 12, 2023 4:05:35 下午 org.apache.catalina.core.ApplicationContext log</span><br><span class="line">信息: Initializing Spring embedded WebApplicationContext</span><br><span class="line">[INFO ] 16:05:35.292 [main] o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 652 ms </span><br><span class="line">4月 12, 2023 4:05:35 下午 org.apache.coyote.AbstractProtocol start</span><br><span class="line">信息: Starting ProtocolHandler [&quot;http-nio-8080&quot;]</span><br><span class="line">[INFO ] 16:05:35.413 [main] o.s.b.w.e.tomcat.TomcatWebServer    - Tomcat started on port(s): 8080 (http) with context path &#x27;&#x27; </span><br><span class="line">name:org.springframework.context.annotation.internalConfigurationAnnotationProcessor 来源：null</span><br><span class="line">name:org.springframework.context.annotation.internalAutowiredAnnotationProcessor 来源：null</span><br><span class="line">name:org.springframework.context.annotation.internalCommonAnnotationProcessor 来源：null</span><br><span class="line">name:org.springframework.context.event.internalEventListenerProcessor 来源：null</span><br><span class="line">name:org.springframework.context.event.internalEventListenerFactory 来源：null</span><br><span class="line">name:a39_3.Config 来源：null</span><br><span class="line">name:bean4 来源：class path resource [b03.xml]</span><br><span class="line">name:bean7 来源：file [F:\Java\黑马全套java教程\第2阶段企业级开发—基础框架\7、spring高级45讲\代码\代码\show\target\classes\com\itheima\a39\sub\Bean7.class]</span><br><span class="line">name:org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory 来源：null</span><br><span class="line">name:bean5 来源：com.itheima.a39.A39_3$Config</span><br><span class="line">name:servletWebServerFactory 来源：com.itheima.a39.A39_3$Config</span><br><span class="line">name:commandLineRunner 来源：com.itheima.a39.A39_3$Config</span><br><span class="line">name:applicationRunner 来源：com.itheima.a39.A39_3$Config</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 12. 执行 runner</span><br><span class="line">commandLineRunner()...[--server.port=8080, debug]</span><br><span class="line">applicationRunner()...[--server.port=8080, debug]</span><br><span class="line">[server.port]</span><br><span class="line">[8080]</span><br><span class="line">[debug]</span><br></pre></td></tr></table></figure>

<p>发现我们手动添加的<code>Bean</code>都已注入，添加的启动参数也能正常读取。</p>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第3步：</p>
<p><code>SpringApplication</code>的<code>run()</code>方法的第3~6步都与一个环境对象有关，环境对象即配置信息的抽象。配置信息有多种来源，例如系统环境变量、<code>properties</code>文件、<code>yaml</code>文件等。环境对象对这些配置信息的整合，将来读取配置时使用此对象即可。</p>
<p>在spring中，环境对象的实现为<code>StandardEnvironment</code>，而在<code>springboot</code>中，环境对象的实现为<code>ApplicationEnvironment</code>类，查看其继承关系：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/14.png" alt="img"></p>
<p>其主要作用就是根据给定键找到对应的值，默认情况下我们创建的环境对象里只有两个来源：</p>
<ol>
<li>系统属性</li>
<li>系统变量</li>
</ol>
<p>我们可以打印一下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step3</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="comment">// 系统环境变量, properties, yaml</span></span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>(); </span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br></pre></td></tr></table></figure>

<p>在查找键的时候，从上到下会有一个优先级，如果有同名的键，优先使用靠前的来源。</p>
<p>我们测试一下优先级：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step3</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;JAVA_HOME&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>控制台输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:\Path\jdk-14.0.1</span><br></pre></td></tr></table></figure>

<p>接下来我们在运行时添加系统属性，即添加虚拟机参数：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">-DJAVA_HOME=abc</span><br></pre></td></tr></table></figure>

<p>再次运行，控制台输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">abc</span><br></pre></td></tr></table></figure>

<p>因此优先查找的来源是<code>systemProperties</code>，其次是<code>systemEnvironment</code>。</p>
<p>我们可以自定义来源：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step3</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">ResourcePropertySource</span>(<span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;step3.properties&quot;</span>)));</span><br><span class="line">        env.getPropertySources().addFirst(<span class="keyword">new</span> <span class="title class_">SimpleCommandLinePropertySource</span>(args));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;server.port&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>优先级为<code>PropertySources</code>中的顺序，排在最前面的优先级最高。</p>
<p>在以上代码中我们在<code>PropertySources</code>添加了配置文件，以及命令行来源，同时配置文件优先级最低，命令行来源优先级最高。</p>
<p>在配置文件中配置<code>server.port=8080</code>，运行代码时添加参数<code>--server.port=8081</code>，运行代码，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">8081</span><br></pre></td></tr></table></figure>

<p>最后输出的是8081，说明命令行来源优先级最高。</p>
<p><code>SpringApplication</code>的<code>run()</code>方法第3步做了以下事情：</p>
<ol>
<li>准备<code>ApplicationEnvironment</code>对象</li>
<li>添加了<code>SimpleCommandLinePropertySource</code>来源</li>
</ol>
<blockquote>
<p>添加<code>ResourcePropertySource</code>来源是后续步骤做的事情。</p>
</blockquote>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第4步：</p>
<p>我们先看一个例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step4</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, NoSuchFieldException &#123;</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ResourcePropertySource</span>(<span class="string">&quot;step4&quot;</span>, <span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;step4.properties&quot;</span>))</span><br><span class="line">        );</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;user.first-name&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;user.middle-name&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;user.last-name&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>step4.properties</code>配置文件的内容为：</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">user.first-name</span>=<span class="string">George</span></span><br><span class="line"><span class="attr">user.middle_name</span>=<span class="string">Walker</span></span><br><span class="line"><span class="attr">user.lastName</span>=<span class="string">Bush</span></span><br></pre></td></tr></table></figure>

<p>我们发现代码中读取的键与配置文件中的键是不一样的，因此读取不到：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">George</span><br><span class="line">null</span><br><span class="line">null</span><br></pre></td></tr></table></figure>

<p>第4步加入了一个特殊的<code>PropertySource</code>源，这个特殊的源就是将配置文件的命名统一成由<code>-</code>号分割的，我们此源：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step4</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException, NoSuchFieldException &#123;</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ResourcePropertySource</span>(<span class="string">&quot;step4&quot;</span>, <span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;step4.properties&quot;</span>))</span><br><span class="line">        );</span><br><span class="line">        ConfigurationPropertySources.attach(env);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;user.first-name&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;user.middle-name&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;user.last-name&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">ConfigurationPropertySourcesPropertySource &#123;name=&#x27;configurationProperties&#x27;&#125;</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">ResourcePropertySource &#123;name=&#x27;step4&#x27;&#125;</span><br><span class="line">George</span><br><span class="line">Walker</span><br><span class="line">Bush</span><br></pre></td></tr></table></figure>

<p>发现一共有四个来源，并且配置文件也能正确读取。</p>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第5步：</p>
<p>第5步是对<code>ApplicationEnvironment</code>再做进一步的增强处理，在给其补充一些新的<code>PropertySource</code>。它是通过<code>Environment</code>后处理器来完成的，因此它具备一定的扩展性，可以由我们自己补充新的实现。</p>
<p>值得提到的是，读取<code>application.properties</code>的源就是在第5步被加入的。</p>
<p>我们查看对应的接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">EnvironmentPostProcessor</span> &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">postProcessEnvironment</span><span class="params">(ConfigurableEnvironment environment, SpringApplication application)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>EnvironmentPostProcessor</code>有一个重要实现<code>ConfigDataEnvironmentPostProcessor</code>，其作用就是读取<code>application.properties</code>中的内容。</p>
<p>演示<code>ConfigDataEnvironmentPostProcessor</code>的作用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step5</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强前&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">ConfigDataEnvironmentPostProcessor</span> <span class="variable">postProcessor1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ConfigDataEnvironmentPostProcessor</span>(<span class="keyword">new</span> <span class="title class_">DeferredLogs</span>(), <span class="keyword">new</span> <span class="title class_">DefaultBootstrapContext</span>());</span><br><span class="line">        postProcessor1.postProcessEnvironment(env,app);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强后&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;server.port&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强前</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强后</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">OriginTrackedMapPropertySource &#123;name=&#x27;Config resource &#x27;class path resource [application.properties]&#x27; via location &#x27;optional:classpath:/&#x27;&#x27;&#125;</span><br><span class="line">8080</span><br></pre></td></tr></table></figure>

<p>增强后多了一个<code>PropertySource</code>源，同时也能读取配置文件中的内容。</p>
<p>我们也可以测试另一个实现<code>RandomValuePropertySourceEnvironmentPostProcessor</code>，这个实现可以随机产生值，我们只需要读取<code>random</code>开头的键即可。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step5</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强前&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">RandomValuePropertySourceEnvironmentPostProcessor</span> <span class="variable">postProcessor2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RandomValuePropertySourceEnvironmentPostProcessor</span>(<span class="keyword">new</span> <span class="title class_">DeferredLog</span>());</span><br><span class="line">        postProcessor2.postProcessEnvironment(env, app);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强后&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;random.int&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;random.int&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;random.int&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;random.uuid&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;random.uuid&quot;</span>));</span><br><span class="line">        System.out.println(env.getProperty(<span class="string">&quot;random.uuid&quot;</span>));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强前</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强后</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">RandomValuePropertySource &#123;name=&#x27;random&#x27;&#125;</span><br><span class="line">2112811766</span><br><span class="line">-176261027</span><br><span class="line">1122993668</span><br><span class="line">115cf977-b2c4-4853-bd79-1ce543ace40c</span><br><span class="line">173de9ff-b916-4bfb-b426-eed474e518ee</span><br><span class="line">ff051847-0940-4664-89b4-41bc9f042dde</span><br></pre></td></tr></table></figure>

<p><code>springboot</code>会在<code>spring.factories</code>配置文件中读取所有的<code>EnvironmentPostProcessor</code>：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># Environment Post Processors</span></span><br><span class="line">org.springframework.boot.env.EnvironmentPostProcessor=\</span><br><span class="line">org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor,\</span><br><span class="line">org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor,\</span><br><span class="line">org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor,\</span><br><span class="line">org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor,\</span><br><span class="line">org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor,\</span><br><span class="line">org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor</span><br></pre></td></tr></table></figure>

<p>我们手动获取这些<code>EnvironmentPostProcessor</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step5</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        List&lt;String&gt; names = SpringFactoriesLoader.loadFactoryNames(EnvironmentPostProcessor.class, Step5.class.getClassLoader());</span><br><span class="line">        <span class="keyword">for</span> (String name : names) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor</span><br><span class="line">org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor</span><br><span class="line">org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor</span><br><span class="line">org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor</span><br><span class="line">org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor</span><br><span class="line">org.springframework.boot.reactor.DebugAgentEnvironmentPostProcessor</span><br><span class="line">org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor</span><br></pre></td></tr></table></figure>

<p>这些<code>EnvironmentPostProcessor</code>的增强方法是通过监听器的方式来调用的。同样，监听器的实现也在<code>spring.factories</code>配置文件中：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># Application Listeners</span><br><span class="line">org.springframework.context.ApplicationListener=\</span><br><span class="line">org.springframework.boot.ClearCachesApplicationListener,\</span><br><span class="line">org.springframework.boot.builder.ParentContextCloserApplicationListener,\</span><br><span class="line">org.springframework.boot.context.FileEncodingApplicationListener,\</span><br><span class="line">org.springframework.boot.context.config.AnsiOutputApplicationListener,\</span><br><span class="line">org.springframework.boot.context.config.DelegatingApplicationListener,\</span><br><span class="line">org.springframework.boot.context.logging.LoggingApplicationListener,\</span><br><span class="line">org.springframework.boot.env.EnvironmentPostProcessorApplicationListener</span><br></pre></td></tr></table></figure>

<p>其中<code>EnvironmentPostProcessorApplicationListener</code>就是去调用各个<code>EnvironmentPostProcessor</code>的<code>postProcessEnvironment()</code>增强方法。</p>
<p>我们使用<code>EnvironmentPostProcessorApplicationListener</code>手动去增加<code>PropertySource</code>源：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step5</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">app</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        app.addListeners(<span class="keyword">new</span> <span class="title class_">EnvironmentPostProcessorApplicationListener</span>());</span><br><span class="line">        <span class="type">EventPublishingRunListener</span> <span class="variable">publisher</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">EventPublishingRunListener</span>(app, args);</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强前&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line">        publisher.environmentPrepared(<span class="keyword">new</span> <span class="title class_">DefaultBootstrapContext</span>(), env);</span><br><span class="line">        System.out.println(<span class="string">&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强后&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (PropertySource&lt;?&gt; ps : env.getPropertySources()) &#123;</span><br><span class="line">            System.out.println(ps);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强前</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">SystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; 增强后</span><br><span class="line">PropertiesPropertySource &#123;name=&#x27;systemProperties&#x27;&#125;</span><br><span class="line">OriginAwareSystemEnvironmentPropertySource &#123;name=&#x27;systemEnvironment&#x27;&#125;</span><br><span class="line">RandomValuePropertySource &#123;name=&#x27;random&#x27;&#125;</span><br><span class="line">OriginTrackedMapPropertySource &#123;name=&#x27;Config resource &#x27;class path resource [application.properties]&#x27; via location &#x27;optional:classpath:/&#x27;&#x27;&#125;</span><br></pre></td></tr></table></figure>

<p>发现增强后有些<code>PropertySource</code>源不生效，是与初始化环境有关。</p>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第6步：</p>
<p><code>@ConfigurationProperties</code>注解的原理是使用<code>Binder</code>进行绑定：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step6</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">ResourcePropertySource</span>(<span class="string">&quot;step4&quot;</span>, <span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;step4.properties&quot;</span>)));</span><br><span class="line">        <span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> Binder.get(env).bind(<span class="string">&quot;user&quot;</span>, User.class).get();</span><br><span class="line">        System.out.println(user);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">User</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> String firstName;</span><br><span class="line">        <span class="keyword">private</span> String middleName;</span><br><span class="line">        <span class="keyword">private</span> String lastName;</span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getFirstName</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> firstName;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setFirstName</span><span class="params">(String firstName)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.firstName = firstName;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getMiddleName</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> middleName;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMiddleName</span><span class="params">(String middleName)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.middleName = middleName;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">getLastName</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> lastName;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLastName</span><span class="params">(String lastName)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.lastName = lastName;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;User&#123;&quot;</span> +</span><br><span class="line">                   <span class="string">&quot;firstName=&#x27;&quot;</span> + firstName + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                   <span class="string">&quot;, middleName=&#x27;&quot;</span> + middleName + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                   <span class="string">&quot;, lastName=&#x27;&quot;</span> + lastName + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                   <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">User&#123;firstName=&#x27;George&#x27;, middleName=&#x27;Walker&#x27;, lastName=&#x27;Bush&#x27;&#125;</span><br></pre></td></tr></table></figure>

<p><code>step4.properties</code>配置文件中的信息已经成功绑定到<code>User</code>对象上</p>
<p>也可以使用另一种方式绑定数据到已有的对象上</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">User</span> <span class="variable">user</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line">Binder.get(env).bind(<span class="string">&quot;user&quot;</span>, Bindable.ofInstance(user));</span><br><span class="line">System.out.println(user);</span><br></pre></td></tr></table></figure>

<p><code>SpringApplication</code>的<code>run()</code>方法第6步的作用就是将配置文件或者环境变量中的这些键值绑定到<code>SpringApplication</code>属性中。</p>
<p>演示示例，准备配置文件，这些属性在<code>SpringApplication</code>都有对应值：</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring.main.banner-mode</span>=<span class="string">off</span></span><br><span class="line"><span class="attr">spring.main.lazy-initialization</span>=<span class="string">true</span></span><br></pre></td></tr></table></figure>

<p>编写代码绑定数据：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step6</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">SpringApplication</span> <span class="variable">application</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplication</span>();</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">ResourcePropertySource</span>(<span class="string">&quot;step6&quot;</span>, <span class="keyword">new</span> <span class="title class_">ClassPathResource</span>(<span class="string">&quot;step6.properties&quot;</span>)));</span><br><span class="line">        System.out.println(application);</span><br><span class="line">        Binder.get(env).bind(<span class="string">&quot;spring.main&quot;</span>, Bindable.ofInstance(application));</span><br><span class="line">        System.out.println(application);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>绑定之前的值：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/15.png" alt="img"></p>
<p>绑定之后的值：</p>
<p><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/16.png" alt="img"></p>
<p>演示<code>SpringApplication</code>的<code>run()</code>方法第7步：</p>
<p><code>SpringApplication</code>的<code>run()</code>方法第7步主要是输出<code>Banner</code>信息，<code>springboot</code>中有默认的实现<code>SpringBootBanner</code>，我们也可以配置自己的<code>Banner</code>信息：</p>
<ol>
<li>配置文字<code>Banner</code>：指定<code>spring.banner.location</code>值，即新的<code>Banner</code></li>
<li>配置图片<code>Banner</code>：指定<code>spring.banner.image.location</code>值，最终图片会转换为文字。</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Step7</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ApplicationEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ApplicationEnvironment</span>();</span><br><span class="line">        <span class="type">SpringApplicationBannerPrinter</span> <span class="variable">printer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SpringApplicationBannerPrinter</span>(</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">DefaultResourceLoader</span>(),</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">SpringBootBanner</span>()</span><br><span class="line">        );</span><br><span class="line">        <span class="comment">// 测试文字 banner</span></span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">MapPropertySource</span>(<span class="string">&quot;custom&quot;</span>, Map.of(<span class="string">&quot;spring.banner.location&quot;</span>,<span class="string">&quot;banner1.txt&quot;</span>)));</span><br><span class="line">        <span class="comment">// 测试图片 banner</span></span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">MapPropertySource</span>(<span class="string">&quot;custom&quot;</span>, Map.of(<span class="string">&quot;spring.banner.image.location&quot;</span>,<span class="string">&quot;banner2.png&quot;</span>)));</span><br><span class="line">        <span class="comment">// 版本号的获取</span></span><br><span class="line">        System.out.println(SpringBootVersion.getVersion());</span><br><span class="line">        printer.print(env, Step7.class, System.out);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>最后回顾一下<code>SpringApplication</code>的<code>run()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> ConfigurableApplicationContext <span class="title function_">run</span><span class="params">(String... args)</span> &#123;</span><br><span class="line">    <span class="type">StopWatch</span> <span class="variable">stopWatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StopWatch</span>();</span><br><span class="line">    stopWatch.start();</span><br><span class="line">    <span class="type">DefaultBootstrapContext</span> <span class="variable">bootstrapContext</span> <span class="operator">=</span> <span class="built_in">this</span>.createBootstrapContext();</span><br><span class="line">    <span class="type">ConfigurableApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="built_in">this</span>.configureHeadlessProperty();</span><br><span class="line">    <span class="comment">// 1.获取事件发布器对象</span></span><br><span class="line">    <span class="type">SpringApplicationRunListeners</span> <span class="variable">listeners</span> <span class="operator">=</span> <span class="built_in">this</span>.getRunListeners(args);</span><br><span class="line">    <span class="comment">// 发布starting事件</span></span><br><span class="line">    listeners.starting(bootstrapContext, <span class="built_in">this</span>.mainApplicationClass);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 2.将main方法传入的args参数封装为ApplicationArguments，分为选项参数(以--开头)与非选项参数</span></span><br><span class="line">        <span class="type">ApplicationArguments</span> <span class="variable">applicationArguments</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DefaultApplicationArguments</span>(args);</span><br><span class="line">        <span class="comment">// 3. 创建Environment并添加基于命令行的源 </span></span><br><span class="line">        <span class="comment">// 4.统一命名配置文件中的键</span></span><br><span class="line">        <span class="comment">// 5.通过事件发布与响应为Environment添加了更多的源</span></span><br><span class="line">        <span class="comment">// 6.将所有以spring.main开头的配置与SpringApplication对象进行绑定</span></span><br><span class="line">        <span class="type">ConfigurableEnvironment</span> <span class="variable">environment</span> <span class="operator">=</span> <span class="built_in">this</span>.prepareEnvironment(listeners, bootstrapContext, applicationArguments);</span><br><span class="line">        <span class="built_in">this</span>.configureIgnoreBeanInfo(environment);</span><br><span class="line">        <span class="comment">// 7.打印Banner信息</span></span><br><span class="line">        <span class="type">Banner</span> <span class="variable">printedBanner</span> <span class="operator">=</span> <span class="built_in">this</span>.printBanner(environment);</span><br><span class="line">        <span class="comment">// 8.创建spring容器</span></span><br><span class="line">        context = <span class="built_in">this</span>.createApplicationContext();</span><br><span class="line">        context.setApplicationStartup(<span class="built_in">this</span>.applicationStartup);</span><br><span class="line">        <span class="comment">// 9.应用初始化器，对context进行增强</span></span><br><span class="line">        <span class="comment">// 10.得到所有的BeanDefinition源，例如xml配置文件、注解配置</span></span><br><span class="line">        <span class="built_in">this</span>.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);</span><br><span class="line">        <span class="comment">// 11.调用ApplicationContext的refresh方法，refresh()中就会调用各种BeanFactory后处理器、Bean后处理器、初始化单例</span></span><br><span class="line">        <span class="built_in">this</span>.refreshContext(context);</span><br><span class="line">        <span class="built_in">this</span>.afterRefresh(context, applicationArguments);</span><br><span class="line">        stopWatch.stop();</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.logStartupInfo) &#123;</span><br><span class="line">            (<span class="keyword">new</span> <span class="title class_">StartupInfoLogger</span>(<span class="built_in">this</span>.mainApplicationClass)).logStarted(<span class="built_in">this</span>.getApplicationLog(), stopWatch);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 发布started事件</span></span><br><span class="line">        listeners.started(context);</span><br><span class="line">        <span class="comment">// 调用所有实现了ApplicationRunner或CommandLineRunner的Runner</span></span><br><span class="line">        <span class="built_in">this</span>.callRunners(context, applicationArguments);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable var10) &#123;</span><br><span class="line">        <span class="comment">// 如果有异常，在handleRunFailure方法中发布failed事件</span></span><br><span class="line">        <span class="built_in">this</span>.handleRunFailure(context, var10, listeners);</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(var10);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 发布running事件</span></span><br><span class="line">        listeners.running(context);</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Throwable var9) &#123;</span><br><span class="line">        <span class="built_in">this</span>.handleRunFailure(context, var9, (SpringApplicationRunListeners)<span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalStateException</span>(var9);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>prepareEnvironment()</code>方法详情：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> ConfigurableEnvironment <span class="title function_">prepareEnvironment</span><span class="params">(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments)</span> &#123;</span><br><span class="line">    <span class="type">ConfigurableEnvironment</span> <span class="variable">environment</span> <span class="operator">=</span> <span class="built_in">this</span>.getOrCreateEnvironment();</span><br><span class="line">    <span class="comment">// 3.创建Environment并添加基于命令行的源</span></span><br><span class="line">    <span class="built_in">this</span>.configureEnvironment((ConfigurableEnvironment)environment, applicationArguments.getSourceArgs());</span><br><span class="line">    <span class="comment">// 4.统一命名配置文件中的键，转换为以-分割的格式</span></span><br><span class="line">    ConfigurationPropertySources.attach((Environment)environment);</span><br><span class="line">    <span class="comment">// 5.通过事件发布与响应为Environment添加了更多的源</span></span><br><span class="line">    listeners.environmentPrepared(bootstrapContext, (ConfigurableEnvironment)environment);</span><br><span class="line">    DefaultPropertiesPropertySource.moveToEnd((ConfigurableEnvironment)environment);</span><br><span class="line">    Assert.state(!((ConfigurableEnvironment)environment).containsProperty(<span class="string">&quot;spring.main.environment-prefix&quot;</span>), <span class="string">&quot;Environment prefix cannot be set via properties.&quot;</span>);</span><br><span class="line">    <span class="comment">// 6.将所有以spring.main开头的配置与SpringApplication对象进行绑定</span></span><br><span class="line">    <span class="built_in">this</span>.bindToSpringApplication((ConfigurableEnvironment)environment);</span><br><span class="line">    <span class="keyword">if</span> (!<span class="built_in">this</span>.isCustomEnvironment) &#123;</span><br><span class="line">        environment = (<span class="keyword">new</span> <span class="title class_">EnvironmentConverter</span>(<span class="built_in">this</span>.getClassLoader())).convertEnvironmentIfNecessary((ConfigurableEnvironment)environment, <span class="built_in">this</span>.deduceEnvironmentClass());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    ConfigurationPropertySources.attach((Environment)environment);</span><br><span class="line">    <span class="keyword">return</span> (ConfigurableEnvironment)environment;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>prepareContext()</code>方法详情：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">prepareContext</span><span class="params">(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner)</span> &#123;</span><br><span class="line">    context.setEnvironment(environment);</span><br><span class="line">    <span class="built_in">this</span>.postProcessApplicationContext(context);</span><br><span class="line">    <span class="comment">// 9.应用初始化器，对context进行增强</span></span><br><span class="line">    <span class="built_in">this</span>.applyInitializers(context);</span><br><span class="line">    <span class="comment">// 发布contextPrepared事件</span></span><br><span class="line">    listeners.contextPrepared(context);</span><br><span class="line">    bootstrapContext.close(context);</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.logStartupInfo) &#123;</span><br><span class="line">        <span class="built_in">this</span>.logStartupInfo(context.getParent() == <span class="literal">null</span>);</span><br><span class="line">        <span class="built_in">this</span>.logStartupProfileInfo(context);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">ConfigurableListableBeanFactory</span> <span class="variable">beanFactory</span> <span class="operator">=</span> context.getBeanFactory();</span><br><span class="line">    beanFactory.registerSingleton(<span class="string">&quot;springApplicationArguments&quot;</span>, applicationArguments);</span><br><span class="line">    <span class="keyword">if</span> (printedBanner != <span class="literal">null</span>) &#123;</span><br><span class="line">        beanFactory.registerSingleton(<span class="string">&quot;springBootBanner&quot;</span>, printedBanner);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (beanFactory <span class="keyword">instanceof</span> DefaultListableBeanFactory) &#123;</span><br><span class="line">        ((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(<span class="built_in">this</span>.allowBeanDefinitionOverriding);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.lazyInitialization) &#123;</span><br><span class="line">        context.addBeanFactoryPostProcessor(<span class="keyword">new</span> <span class="title class_">LazyInitializationBeanFactoryPostProcessor</span>());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 10.得到所有的BeanDefinition源，例如xml配置文件、注解配置</span></span><br><span class="line">    Set&lt;Object&gt; sources = <span class="built_in">this</span>.getAllSources();</span><br><span class="line">    Assert.notEmpty(sources, <span class="string">&quot;Sources must not be empty&quot;</span>);</span><br><span class="line">    <span class="comment">// 加载BeanDefinition到ApplicationContext容器中</span></span><br><span class="line">    <span class="built_in">this</span>.load(context, sources.toArray(<span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]));</span><br><span class="line">    <span class="comment">// 发布contextLoaded事件</span></span><br><span class="line">    listeners.contextLoaded(context);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="Tomcat内嵌容器"><a href="#Tomcat内嵌容器" class="headerlink" title="Tomcat内嵌容器"></a><strong>Tomcat内嵌容器</strong></h1><h2 id="Tomcat内嵌容器-1"><a href="#Tomcat内嵌容器-1" class="headerlink" title="Tomcat内嵌容器"></a>Tomcat内嵌容器</h2><p><code>Tomcat</code>基本结构：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">Server</span><br><span class="line">└───Service</span><br><span class="line">    ├───Connector (协议, 端口)</span><br><span class="line">    └───Engine</span><br><span class="line">        └───Host(虚拟主机 localhost)</span><br><span class="line">            ├───Context1 (应用1, 可以设置虚拟路径, / 即 url 起始路径; 项目磁盘路径, 即 docBase )</span><br><span class="line">            │   │   index.html</span><br><span class="line">            │   └───WEB-INF</span><br><span class="line">            │       │   web.xml (servlet, filter, listener) 3.0之后不需要web.xml，可以通过编程的方式实现</span><br><span class="line">            │       ├───classes (servlet, controller, service ...)</span><br><span class="line">            │       ├───jsp</span><br><span class="line">            │       └───lib (第三方 jar 包)</span><br><span class="line">            └───Context2 (应用2)</span><br><span class="line">                │   index.html</span><br><span class="line">                └───WEB-INF</span><br><span class="line">                        web.xml</span><br></pre></td></tr></table></figure>

<p>创建内嵌的<code>Tomcat</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        resp.setContentType(<span class="string">&quot;text/html;charset=utf-8&quot;</span>);</span><br><span class="line">        resp.getWriter().print(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                &lt;h3&gt;hello&lt;/h3&gt;</span></span><br><span class="line"><span class="string">                &quot;&quot;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestTomcat</span> &#123;</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> LifecycleException, IOException &#123;</span><br><span class="line">        <span class="comment">// 1.创建 Tomcat 对象</span></span><br><span class="line">        <span class="type">Tomcat</span> <span class="variable">tomcat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Tomcat</span>();</span><br><span class="line">        tomcat.setBaseDir(<span class="string">&quot;tomcat&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 2.创建项目文件夹, 即 docBase 文件夹</span></span><br><span class="line">        <span class="type">File</span> <span class="variable">docBase</span> <span class="operator">=</span> Files.createTempDirectory(<span class="string">&quot;boot.&quot;</span>).toFile();</span><br><span class="line">        docBase.deleteOnExit();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context</span></span><br><span class="line">        <span class="type">Context</span> <span class="variable">context</span> <span class="operator">=</span> tomcat.addContext(<span class="string">&quot;&quot;</span>, docBase.getAbsolutePath());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 4.编程添加 Servlet</span></span><br><span class="line">        context.addServletContainerInitializer(<span class="keyword">new</span> <span class="title class_">ServletContainerInitializer</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onStartup</span><span class="params">(Set&lt;Class&lt;?&gt;&gt; c, ServletContext ctx)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">                <span class="type">HelloServlet</span> <span class="variable">helloServlet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloServlet</span>();</span><br><span class="line">                ctx.addServlet(<span class="string">&quot;aaa&quot;</span>, helloServlet).addMapping(<span class="string">&quot;/hello&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, Collections.emptySet());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 5.启动 Tomcat</span></span><br><span class="line">        tomcat.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 6.创建连接器, 设置监听端口</span></span><br><span class="line">        <span class="type">Connector</span> <span class="variable">connector</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Connector</span>(<span class="keyword">new</span> <span class="title class_">Http11Nio2Protocol</span>());</span><br><span class="line">        connector.setPort(<span class="number">8080</span>);</span><br><span class="line">        tomcat.setConnector(connector);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>启动<code>Tomcat</code>容器，访问<a target="_blank" rel="noopener" href="http://localhost:8080/hello%EF%BC%8C%E6%B5%8F%E8%A7%88%E5%99%A8%E8%BE%93%E5%87%BA%EF%BC%9A">http://localhost:8080/hello，浏览器输出：</a></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hello</span><br></pre></td></tr></table></figure>

<h2 id="Spring集成内嵌Tomcat"><a href="#Spring集成内嵌Tomcat" class="headerlink" title="Spring集成内嵌Tomcat"></a><strong>Spring集成内嵌Tomcat</strong></h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HelloServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException &#123;</span><br><span class="line">        resp.setContentType(<span class="string">&quot;text/html;charset=utf-8&quot;</span>);</span><br><span class="line">        resp.getWriter().print(<span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">                &lt;h3&gt;hello&lt;/h3&gt;</span></span><br><span class="line"><span class="string">                &quot;&quot;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestTomcat</span> &#123;</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> LifecycleException, IOException &#123;</span><br><span class="line">        <span class="comment">// 1.创建 Tomcat 对象</span></span><br><span class="line">        <span class="type">Tomcat</span> <span class="variable">tomcat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Tomcat</span>();</span><br><span class="line">        tomcat.setBaseDir(<span class="string">&quot;tomcat&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 2.创建项目文件夹, 即 docBase 文件夹</span></span><br><span class="line">        <span class="type">File</span> <span class="variable">docBase</span> <span class="operator">=</span> Files.createTempDirectory(<span class="string">&quot;boot.&quot;</span>).toFile();</span><br><span class="line">        docBase.deleteOnExit();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 3.创建 Tomcat 项目, 在 Tomcat 中称为 Context</span></span><br><span class="line">        <span class="type">Context</span> <span class="variable">context</span> <span class="operator">=</span> tomcat.addContext(<span class="string">&quot;&quot;</span>, docBase.getAbsolutePath());</span><br><span class="line"></span><br><span class="line">        <span class="type">WebApplicationContext</span> <span class="variable">springContext</span> <span class="operator">=</span> getApplicationContext();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 4.编程添加 Servlet</span></span><br><span class="line">        context.addServletContainerInitializer(<span class="keyword">new</span> <span class="title class_">ServletContainerInitializer</span>() &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onStartup</span><span class="params">(Set&lt;Class&lt;?&gt;&gt; c, ServletContext ctx)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">                <span class="type">HelloServlet</span> <span class="variable">helloServlet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloServlet</span>();</span><br><span class="line">                ctx.addServlet(<span class="string">&quot;aaa&quot;</span>, helloServlet).addMapping(<span class="string">&quot;/hello&quot;</span>);</span><br><span class="line">                <span class="comment">// 首先匹配&quot;/hello&quot;路径，如果匹配不上，则进入&quot;/&quot;匹配</span></span><br><span class="line">                <span class="type">DispatcherServlet</span> <span class="variable">dispatcherServlet</span> <span class="operator">=</span> springContext.getBean(DispatcherServlet.class);</span><br><span class="line">                ctx.addServlet(<span class="string">&quot;dispatcherServlet&quot;</span>, dispatcherServlet).addMapping(<span class="string">&quot;/&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, Collections.emptySet());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 5.启动 Tomcat</span></span><br><span class="line">        tomcat.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 6.创建连接器, 设置监听端口</span></span><br><span class="line">        <span class="type">Connector</span> <span class="variable">connector</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Connector</span>(<span class="keyword">new</span> <span class="title class_">Http11Nio2Protocol</span>());</span><br><span class="line">        connector.setPort(<span class="number">8080</span>);</span><br><span class="line">        tomcat.setConnector(connector);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> WebApplicationContext <span class="title function_">getApplicationContext</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// AnnotationConfigServletWebServerApplicationContext已经内嵌了Tomcat因此不用</span></span><br><span class="line">        <span class="type">AnnotationConfigWebApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigWebApplicationContext</span>();</span><br><span class="line">        context.register(Config.class);</span><br><span class="line">        context.refresh();</span><br><span class="line">        <span class="keyword">return</span> context;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> DispatcherServletRegistrationBean <span class="title function_">registrationBean</span><span class="params">(DispatcherServlet dispatcherServlet)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DispatcherServletRegistrationBean</span>(dispatcherServlet, <span class="string">&quot;/&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="comment">// 这个例子中必须为 DispatcherServlet 提供 AnnotationConfigWebApplicationContext, 否则会选择 XmlWebApplicationContext 实现</span></span><br><span class="line">        <span class="keyword">public</span> DispatcherServlet <span class="title function_">dispatcherServlet</span><span class="params">(WebApplicationContext applicationContext)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">DispatcherServlet</span>(applicationContext);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> RequestMappingHandlerAdapter <span class="title function_">requestMappingHandlerAdapter</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="type">RequestMappingHandlerAdapter</span> <span class="variable">handlerAdapter</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">RequestMappingHandlerAdapter</span>();</span><br><span class="line">            handlerAdapter.setMessageConverters(List.of(<span class="keyword">new</span> <span class="title class_">MappingJackson2HttpMessageConverter</span>()));</span><br><span class="line">            <span class="keyword">return</span> handlerAdapter;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@RestController</span></span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyController</span> &#123;</span><br><span class="line">            <span class="meta">@GetMapping(&quot;hello2&quot;)</span></span><br><span class="line">            <span class="keyword">public</span> Map&lt;String,Object&gt; <span class="title function_">hello</span><span class="params">()</span> &#123;</span><br><span class="line">                <span class="keyword">return</span> Map.of(<span class="string">&quot;hello2&quot;</span>, <span class="string">&quot;hello2, spring!&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>启动<code>Tomcat</code>容器，访问<a target="_blank" rel="noopener" href="http://localhost:8080/hello2%EF%BC%8C%E6%B5%8F%E8%A7%88%E5%99%A8%E8%BE%93%E5%87%BA%EF%BC%9A">http://localhost:8080/hello2，浏览器输出：</a></p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;hello2&quot;</span><span class="punctuation">:</span> <span class="string">&quot;hello2, spring!&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>在<code>context</code>的<code>addServletContainerInitializer()</code>方法我们注册<code>Servlet</code>不够通用，应该交由<code>ServletRegistrationBean</code>进行注册：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">context.addServletContainerInitializer(<span class="keyword">new</span> <span class="title class_">ServletContainerInitializer</span>() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onStartup</span><span class="params">(Set&lt;Class&lt;?&gt;&gt; c, ServletContext ctx)</span> <span class="keyword">throws</span> ServletException &#123;</span><br><span class="line">        <span class="type">HelloServlet</span> <span class="variable">helloServlet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HelloServlet</span>();</span><br><span class="line">        ctx.addServlet(<span class="string">&quot;aaa&quot;</span>, helloServlet).addMapping(<span class="string">&quot;/hello&quot;</span>);</span><br><span class="line">        <span class="keyword">for</span> (ServletRegistrationBean registrationBean : springContext.getBeansOfType(ServletRegistrationBean.class).values()) &#123;</span><br><span class="line">            registrationBean.onStartup(ctx);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, Collections.emptySet());</span><br></pre></td></tr></table></figure>

<p><code>springboot</code>在整合<code>tomcat</code>时，先创建<code>spring</code>容器，在调用<code>refresh()</code>方法；<code>refresh()</code>方法中的<code>onRefresh()</code>就是以上演示的1~4步，最后在<code>finishRefresh()</code>方法中启动<code>tomcat</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">refresh</span><span class="params">()</span> <span class="keyword">throws</span> BeansException, IllegalStateException &#123;</span><br><span class="line">    <span class="keyword">synchronized</span>(<span class="built_in">this</span>.startupShutdownMonitor) &#123;</span><br><span class="line">        <span class="type">StartupStep</span> <span class="variable">contextRefresh</span> <span class="operator">=</span> <span class="built_in">this</span>.applicationStartup.start(<span class="string">&quot;spring.context.refresh&quot;</span>);</span><br><span class="line">        <span class="built_in">this</span>.prepareRefresh();</span><br><span class="line">        <span class="type">ConfigurableListableBeanFactory</span> <span class="variable">beanFactory</span> <span class="operator">=</span> <span class="built_in">this</span>.obtainFreshBeanFactory();</span><br><span class="line">        <span class="built_in">this</span>.prepareBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.postProcessBeanFactory(beanFactory);</span><br><span class="line">            <span class="type">StartupStep</span> <span class="variable">beanPostProcess</span> <span class="operator">=</span> <span class="built_in">this</span>.applicationStartup.start(<span class="string">&quot;spring.context.beans.post-process&quot;</span>);</span><br><span class="line">            <span class="built_in">this</span>.invokeBeanFactoryPostProcessors(beanFactory);</span><br><span class="line">            <span class="built_in">this</span>.registerBeanPostProcessors(beanFactory);</span><br><span class="line">            beanPostProcess.end();</span><br><span class="line">            <span class="built_in">this</span>.initMessageSource();</span><br><span class="line">            <span class="built_in">this</span>.initApplicationEventMulticaster();</span><br><span class="line">            <span class="built_in">this</span>.onRefresh();</span><br><span class="line">            <span class="built_in">this</span>.registerListeners();</span><br><span class="line">            <span class="built_in">this</span>.finishBeanFactoryInitialization(beanFactory);</span><br><span class="line">            <span class="built_in">this</span>.finishRefresh();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BeansException var10) &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.logger.isWarnEnabled()) &#123;</span><br><span class="line">                <span class="built_in">this</span>.logger.warn(<span class="string">&quot;Exception encountered during context initialization - cancelling refresh attempt: &quot;</span> + var10);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="built_in">this</span>.destroyBeans();</span><br><span class="line">            <span class="built_in">this</span>.cancelRefresh(var10);</span><br><span class="line">            <span class="keyword">throw</span> var10;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.resetCommonCaches();</span><br><span class="line">            contextRefresh.end();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="Boot自动配置原理"><a href="#Boot自动配置原理" class="headerlink" title="Boot自动配置原理"></a><strong>Boot自动配置原理</strong></h1><h2 id="Boot自动配置原理-1"><a href="#Boot自动配置原理-1" class="headerlink" title="Boot自动配置原理"></a>Boot自动配置原理</h2><p>手动导入第三方配置类的通用方法，可以使用<code>@Import</code>注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A41_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericApplicationContext</span>();</span><br><span class="line">        context.registerBean(<span class="string">&quot;config&quot;</span>, Config.class);</span><br><span class="line">        context.registerBean(ConfigurationClassPostProcessor.class);</span><br><span class="line">        context.refresh();</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 本项目的配置类</span></span><br><span class="line">    <span class="meta">@Import(&#123;AutoConfiguration1.class,AutoConfiguration2.class&#125;)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration1</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Bean1</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Bean1</span><span class="params">(String name)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.name = name;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;Bean1&#123;&quot;</span> +</span><br><span class="line">                   <span class="string">&quot;name=&#x27;&quot;</span> + name + <span class="string">&#x27;\&#x27;&#x27;</span> +</span><br><span class="line">                   <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration2</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">config</span><br><span class="line">org.springframework.context.annotation.ConfigurationClassPostProcessor</span><br><span class="line">com.itheima.a41.A41_1$AutoConfiguration1</span><br><span class="line">bean1</span><br><span class="line">com.itheima.a41.A41_1$AutoConfiguration2</span><br><span class="line">bean2</span><br></pre></td></tr></table></figure>

<p>第三方的配置类也已经加入容器中。</p>
<p>以上代码有一定的局限性，因为第三方的配置可能很多，写在代码中不方便管理，希望写在配置中，我们可以实现一个<code>ImportSelector</code>接口：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">ImportSelector</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回值为配置类的类名</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> importingClassMetadata</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()&#125;;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再<code>Config</code>类上添加<code>@Import(MyImportSelector.class)</code>注解，此注解会读取<code>MyImportSelector</code>的返回值，并解析返回的配置类。</p>
<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">config</span><br><span class="line">org.springframework.context.annotation.ConfigurationClassPostProcessor</span><br><span class="line">com.itheima.a41.A41_1$AutoConfiguration1</span><br><span class="line">bean1</span><br><span class="line">com.itheima.a41.A41_1$AutoConfiguration2</span><br><span class="line">bean2</span><br></pre></td></tr></table></figure>

<p>效果和最初的一样。</p>
<p>以上代码还可以进一步改进，我们希望配置不写在代码中，而是写在配置文件中。这些配置必须在<code>resources</code>下的名为<code>META-INF</code>目录下的名叫<code>spring.factories</code>的配置文件中。</p>
<p>编写<code>spring.factories</code>配置文件：</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">com.itheima.a41.A41_1$MyImportSelector</span>=<span class="string">\</span></span><br><span class="line"><span class="string">com.itheima.a41.A41_1.AutoConfiguration1,\</span></span><br><span class="line"><span class="string">com.itheima.a41.A41_1.AutoConfiguration2</span></span><br></pre></td></tr></table></figure>

<p>修改<code>MyImportSelector</code>的实现：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">ImportSelector</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回值为配置类的类名</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> importingClassMetadata</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">        List&lt;String&gt; names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, <span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">return</span> names.toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">config</span><br><span class="line">org.springframework.context.annotation.ConfigurationClassPostProcessor</span><br><span class="line">com.itheima.a41.A41_1$AutoConfiguration1</span><br><span class="line">bean1</span><br><span class="line">com.itheima.a41.A41_1$AutoConfiguration2</span><br><span class="line">bean2</span><br></pre></td></tr></table></figure>

<p>也能成功解析第三方配置。</p>
<p><code>SpringFactoriesLoader</code>的扫描范围非常广，不仅扫描当前项目下的<code>spring.factories</code>配置文件，也会扫描依赖包中的<code>spring.factories</code>配置文件。</p>
<p>我们可以打印一下<code>spring</code>的<code>spring.factories</code>配置文件中的部分类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">ImportSelector</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回值为配置类的类名</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> importingClassMetadata</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">        <span class="keyword">for</span> (String name : SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class, <span class="literal">null</span>)) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line">        List&lt;String&gt; names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, <span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">return</span> names.toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.elasticsearch.ReactiveElasticsearchRestClientAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.neo4j.Neo4jReactiveRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.r2dbc.R2dbcDataAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.r2dbc.R2dbcRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.r2dbc.R2dbcTransactionManagerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.rsocket.RSocketMessagingAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.rsocket.RSocketRequesterAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.rsocket.RSocketServerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.rsocket.RSocketStrategiesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.rsocket.RSocketSecurityAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.saml2.Saml2RelyingPartyAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.session.SessionAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration</span><br><span class="line">org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration</span><br><span class="line">org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration</span><br><span class="line">com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure</span><br></pre></td></tr></table></figure>

<p>如果本项目的配置类和第三方的配置类冲突了会怎么样？</p>
<p>我们在本项目中添加一个<code>Bean1</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>(<span class="string">&quot;本项目&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在第三方配置中也添加一个<code>Bean1</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration1</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>(<span class="string">&quot;第三方&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们打印一下<code>Bean1</code>的名称：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(context.getBean(Bean1.class));</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Bean1&#123;name=&#x27;本项目&#x27;&#125;</span><br></pre></td></tr></table></figure>

<p>发现是本项目中的<code>Bean</code>优先生效。</p>
<p>由于<code>@Import()</code>首先解析第三方配置，再解析本项目配置，而<code>spring</code>中默认的<code>BeanFactory</code>支持同类型的<code>Bean</code>覆盖，因此最终是本项目中的<code>Bean</code>优先生效。</p>
<p>但<code>springboot</code>中默认是不允许同名的<code>Bean</code>覆盖的，但可以进行设置：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">context.getDefaultListableBeanFactory().setAllowBeanDefinitionOverriding(<span class="literal">false</span>);</span><br></pre></td></tr></table></figure>

<p>我们先设置为<code>false</code>，即不允许覆盖，查看控制台，发现报错：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Exception in thread &quot;main&quot; org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name &#x27;bean1&#x27; defined in com.itheima.a41.A41_1$Config: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config; factoryMethodName=bean1; initMethodName=null; destroyMethodName=(inferred); defined in com.itheima.a41.A41_1$Config] for bean &#x27;bean1&#x27;: There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.itheima.a41.A41_1$AutoConfiguration1; factoryMethodName=bean1; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/itheima/a41/A41_1$AutoConfiguration1.class]] bound.</span><br><span class="line">        at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:995)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:295)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:153)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:129)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:343)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)</span><br><span class="line">        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)</span><br><span class="line">        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)</span><br><span class="line">        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)</span><br><span class="line">        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)</span><br><span class="line">        at com.itheima.a41.A41_1.main(A41_1.java:20)</span><br></pre></td></tr></table></figure>

<p>在<code>AllowBeanDefinitionOverriding</code>为<code>false</code>的情况下，如果本项目的配置类和第三方的配置类冲突，我们更希望使用本项目的配置，因此我们可以调整<code>@Import()</code>注解解析的顺序，我们可以实现<code>DeferredImportSelector</code>接口。这是一个延迟解析接口，它的解析顺序是先解析本项目的<code>Bean</code>，再去解析第三方的<code>Bean</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回值为配置类的类名</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> importingClassMetadata</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">        List&lt;String&gt; names = SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, <span class="literal">null</span>);</span><br><span class="line">        <span class="keyword">return</span> names.toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再次运行，发现报错：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">Exception in thread &quot;main&quot; org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name &#x27;bean1&#x27; defined in class path resource [com/itheima/a41/A41_1$AutoConfiguration1.class]: Cannot register bean definition [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=com.itheima.a41.A41_1$AutoConfiguration1; factoryMethodName=bean1; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [com/itheima/a41/A41_1$AutoConfiguration1.class]] for bean &#x27;bean1&#x27;: There is already [Root bean: class [null]; scope=; abstract=false; lazyInit=null; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=config; factoryMethodName=bean1; initMethodName=null; destroyMethodName=(inferred); defined in com.itheima.a41.A41_1$Config] bound.</span><br><span class="line">        at org.springframework.beans.factory.support.DefaultListableBeanFactory.registerBeanDefinition(DefaultListableBeanFactory.java:995)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:295)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:153)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:129)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:343)</span><br><span class="line">        at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:247)</span><br><span class="line">        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:311)</span><br><span class="line">        at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:112)</span><br><span class="line">        at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)</span><br><span class="line">        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)</span><br><span class="line">        at com.itheima.a41.A41_1.main(A41_1.java:20)</span><br></pre></td></tr></table></figure>

<p>观察报错，发现首先加载的是本项目中的<code>Bean</code>。</p>
<p>我们可以在第三方配置中添加<code>@ConditionalOnMissingBean</code>注解，也就是本项目没有此类型，才添加。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration1</span> &#123;</span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean</span></span><br><span class="line">    <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>(<span class="string">&quot;第三方&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="经典自动配置实现"><a href="#经典自动配置实现" class="headerlink" title="经典自动配置实现"></a>经典自动配置实现</h2><ol>
<li><strong>AopAutoConfiguration</strong></li>
</ol>
<p>我们先看一个示例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestAopAuto</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericApplicationContext</span>();</span><br><span class="line">        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());</span><br><span class="line">        context.registerBean(Config.class);</span><br><span class="line">        context.refresh();</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="meta">@Import(MyImportSelector.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;AopAutoConfiguration.class.getName()&#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.context.annotation.internalConfigurationAnnotationProcessor</span><br><span class="line">org.springframework.context.annotation.internalAutowiredAnnotationProcessor</span><br><span class="line">org.springframework.context.annotation.internalCommonAnnotationProcessor</span><br><span class="line">org.springframework.context.event.internalEventListenerProcessor</span><br><span class="line">org.springframework.context.event.internalEventListenerFactory</span><br><span class="line">com.itheima.a41.TestAopAuto$Config</span><br><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration</span><br><span class="line">org.springframework.aop.config.internalAutoProxyCreator</span><br><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration</span><br></pre></td></tr></table></figure>

<p>其中：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration</span><br><span class="line">org.springframework.aop.config.internalAutoProxyCreator</span><br><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration</span><br><span class="line">org.springframework.boot.autoconfigure.aop.AopAutoConfiguration</span><br></pre></td></tr></table></figure>

<p>都是<code>AopAutoConfiguration</code>帮我们添加的<code>Bean</code>，接下来我们学习一下这四个<code>Bean</code>是如何添加进容器中的，分别有什么作用。</p>
<p>我们进入<code>AopAutoConfiguration</code>源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="comment">// 在配置文件中找一对键值，键前缀为&quot;spring.aop&quot;，名称为&quot;auto&quot;，值必须为&quot;true&quot;，满足这个条件此配置才生效</span></span><br><span class="line"><span class="comment">// 或者项目中缺失了这个键值也视为生效，对应matchIfMissing = true</span></span><br><span class="line"><span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">    prefix = &quot;spring.aop&quot;,</span></span><br><span class="line"><span class="meta">    name = &#123;&quot;auto&quot;&#125;,</span></span><br><span class="line"><span class="meta">    havingValue = &quot;true&quot;,</span></span><br><span class="line"><span class="meta">    matchIfMissing = true</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AopAutoConfiguration</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AopAutoConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="comment">// 类路径下是否缺失org.aspectj.weaver.Advice类</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingClass(&#123;&quot;org.aspectj.weaver.Advice&quot;&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">        prefix = &quot;spring.aop&quot;,</span></span><br><span class="line"><span class="meta">        name = &#123;&quot;proxy-target-class&quot;&#125;,</span></span><br><span class="line"><span class="meta">        havingValue = &quot;true&quot;,</span></span><br><span class="line"><span class="meta">        matchIfMissing = true</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ClassProxyingConfiguration</span> &#123;</span><br><span class="line">        ClassProxyingConfiguration() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">static</span> BeanFactoryPostProcessor <span class="title function_">forceAutoProxyCreatorToUseClassProxying</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> (beanFactory) -&gt; &#123;</span><br><span class="line">                <span class="keyword">if</span> (beanFactory <span class="keyword">instanceof</span> BeanDefinitionRegistry) &#123;</span><br><span class="line">                    <span class="type">BeanDefinitionRegistry</span> <span class="variable">registry</span> <span class="operator">=</span> (BeanDefinitionRegistry)beanFactory;</span><br><span class="line">                    AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);</span><br><span class="line">                    AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="comment">// 类路径下是否存在Advice类，一般使用spring时类路径下都会有此类</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(&#123;Advice.class&#125;)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AspectJAutoProxyingConfiguration</span> &#123;</span><br><span class="line">        AspectJAutoProxyingConfiguration() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">            proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="comment">// proxyTargetClass为true表示不管类是否实现了接口都采用Cglib的方式创建代理</span></span><br><span class="line">        <span class="comment">// proxyTargetClass为false表示类实现了接口优先使用jdk代理，否则使用Cglib的方式创建代理</span></span><br><span class="line">        <span class="meta">@EnableAspectJAutoProxy(</span></span><br><span class="line"><span class="meta">            proxyTargetClass = true</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="comment">// 在配置文件中找一对键值，键前缀为&quot;spring.aop&quot;，名称为&quot;proxy-target-class&quot;，值必须为&quot;true&quot;</span></span><br><span class="line">        <span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">            prefix = &quot;spring.aop&quot;,</span></span><br><span class="line"><span class="meta">            name = &#123;&quot;proxy-target-class&quot;&#125;,</span></span><br><span class="line"><span class="meta">            havingValue = &quot;true&quot;,</span></span><br><span class="line"><span class="meta">            matchIfMissing = true</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">CglibAutoProxyConfiguration</span> &#123;</span><br><span class="line">            CglibAutoProxyConfiguration() &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">            proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="comment">// 本质上是使用@Import的方式导入配置</span></span><br><span class="line">        <span class="meta">@EnableAspectJAutoProxy(</span></span><br><span class="line"><span class="meta">            proxyTargetClass = false</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="comment">// 在配置文件中找一对键值，键前缀为&quot;spring.aop&quot;，名称为&quot;proxy-target-class&quot;，值必须为&quot;false&quot;</span></span><br><span class="line">        <span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">            prefix = &quot;spring.aop&quot;,</span></span><br><span class="line"><span class="meta">            name = &#123;&quot;proxy-target-class&quot;&#125;,</span></span><br><span class="line"><span class="meta">            havingValue = &quot;false&quot;</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">JdkDynamicAutoProxyConfiguration</span> &#123;</span><br><span class="line">            JdkDynamicAutoProxyConfiguration() &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们可以尝试手动添加配置，将<code>spring.aop.auto</code>置为<code>false</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">StandardEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StandardEnvironment</span>();</span><br><span class="line">env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">SimpleCommandLinePropertySource</span>(<span class="string">&quot;--spring.aop.auto=false&quot;</span>));</span><br><span class="line">context.setEnvironment(env);</span><br></pre></td></tr></table></figure>

<p>查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.context.annotation.internalConfigurationAnnotationProcessor</span><br><span class="line">org.springframework.context.annotation.internalAutowiredAnnotationProcessor</span><br><span class="line">org.springframework.context.annotation.internalCommonAnnotationProcessor</span><br><span class="line">org.springframework.context.event.internalEventListenerProcessor</span><br><span class="line">org.springframework.context.event.internalEventListenerFactory</span><br><span class="line">com.itheima.a41.TestAopAuto$Config</span><br></pre></td></tr></table></figure>

<p>发现<code>AopAutoConfiguration</code>配置已经不生效了。</p>
<p>进入<code>@EnableAspectJAutoProxy</code>注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Import(&#123;AspectJAutoProxyRegistrar.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAspectJAutoProxy &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">proxyTargetClass</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">exposeProxy</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>@Import</code>注解导入<code>AspectJAutoProxyRegistrar</code>类型的配置类，继续进入<code>AspectJAutoProxyRegistrar</code>源码：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* ImportBeanDefinitionRegistrar接口是以编程方式将BeanDefinition加入容器</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">AspectJAutoProxyRegistrar</span> <span class="keyword">implements</span> <span class="title class_">ImportBeanDefinitionRegistrar</span> &#123;</span><br><span class="line">    AspectJAutoProxyRegistrar() &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">        <span class="comment">// 添加ProxyCreator</span></span><br><span class="line">        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);</span><br><span class="line">        <span class="type">AnnotationAttributes</span> <span class="variable">enableAspectJAutoProxy</span> <span class="operator">=</span> AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);</span><br><span class="line">        <span class="keyword">if</span> (enableAspectJAutoProxy != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (enableAspectJAutoProxy.getBoolean(<span class="string">&quot;proxyTargetClass&quot;</span>)) &#123;</span><br><span class="line">                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (enableAspectJAutoProxy.getBoolean(<span class="string">&quot;exposeProxy&quot;</span>)) &#123;</span><br><span class="line">                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们进入<code>registerAspectJAnnotationAutoProxyCreatorIfNecessary()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> BeanDefinition <span class="title function_">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="params">(BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)<span class="literal">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再进入重载的<code>registerAspectJAnnotationAutoProxyCreatorIfNecessary()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> BeanDefinition <span class="title function_">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="params">(BeanDefinitionRegistry registry, <span class="meta">@Nullable</span> Object source)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>此方法加入了<code>AnnotationAwareAspectJAutoProxyCreator</code>，这是一个<code>Bean</code>的后处理器，主要作用就是为了创建代理。</p>
<p>我们可以查看一下本项目使用的代理方式：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AnnotationAwareAspectJAutoProxyCreator</span> <span class="variable">creator</span> <span class="operator">=</span> context.getBean(</span><br><span class="line">        <span class="string">&quot;org.springframework.aop.config.internalAutoProxyCreator&quot;</span>, AnnotationAwareAspectJAutoProxyCreator.class);</span><br><span class="line">System.out.println(creator.isProxyTargetClass());</span><br></pre></td></tr></table></figure>

<p>控制台输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">true</span><br></pre></td></tr></table></figure>

<p><strong>AopAutoConfiguration总结：</strong></p>
<ul>
<li>AOP 自动配置类为 <code>org.springframework.boot.autoconfigure.aop.AopAutoConfiguration</code></li>
<li>可以通过 <code>spring.aop.auto=false</code> 禁用 aop 自动配置</li>
<li>AOP 自动配置的本质是通过 <code>@EnableAspectJAutoProxy</code> 来开启了自动代理，如果在引导类上自己添加了 <code>@EnableAspectJAutoProxy</code> 那么以自己添加的为准</li>
<li><code>@EnableAspectJAutoProxy</code> 的本质是向容器中添加了 <code>AnnotationAwareAspectJAutoProxyCreator</code> 这个<code>bean</code>后处理器，它能够找到容器中所有切面，并为匹配切点的目标类创建代理，创建代理的工作一般是在 <code>bean</code>的初始化阶段完成的</li>
</ul>
<ol start="2">
<li><strong>DataSourceAutoConfiguration</strong></li>
</ol>
<p>编写以下代码，添加<code>DataSourceAutoConfiguration</code>、<code>MybatisAutoConfiguration</code>、<code>DataSourceTransactionManagerAutoConfiguration</code>、<code>TransactionAutoConfiguration</code>等第三方配置。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestDataSourceAuto</span> &#123;</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericApplicationContext</span>();</span><br><span class="line">        <span class="type">StandardEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StandardEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">SimpleCommandLinePropertySource</span>(</span><br><span class="line">                <span class="string">&quot;--spring.datasource.url=jdbc:mysql://localhost:3306/secondhandtradingplatform&quot;</span>,</span><br><span class="line">                <span class="string">&quot;--spring.datasource.username=root&quot;</span>,</span><br><span class="line">                <span class="string">&quot;--spring.datasource.password=2001&quot;</span></span><br><span class="line">        ));</span><br><span class="line">        context.setEnvironment(env);</span><br><span class="line">        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());</span><br><span class="line">        context.registerBean(Config.class);</span><br><span class="line">        context.refresh();</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">resourceDescription</span> <span class="operator">=</span> context.getBeanDefinition(name).getResourceDescription();</span><br><span class="line">            <span class="keyword">if</span> (resourceDescription != <span class="literal">null</span>)</span><br><span class="line">                System.out.println(name + <span class="string">&quot; 来源:&quot;</span> + resourceDescription);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="meta">@Import(MyImportSelector.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;</span><br><span class="line">                    DataSourceAutoConfiguration.class.getName(),</span><br><span class="line">                    MybatisAutoConfiguration.class.getName(),</span><br><span class="line">                    DataSourceTransactionManagerAutoConfiguration.class.getName(),</span><br><span class="line">                    TransactionAutoConfiguration.class.getName()</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>查看输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">dataSource 来源:class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]</span><br><span class="line">hikariPoolDataSourceMetadataProvider 来源:class path resource [org/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration.class]</span><br><span class="line">sqlSessionFactory 来源:class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]</span><br><span class="line">sqlSessionTemplate 来源:class path resource [org/mybatis/spring/boot/autoconfigure/MybatisAutoConfiguration.class]</span><br><span class="line">transactionManager 来源:class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration.class]</span><br><span class="line">org.springframework.transaction.config.internalTransactionAdvisor 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]</span><br><span class="line">transactionAttributeSource 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]</span><br><span class="line">transactionInterceptor 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]</span><br><span class="line">org.springframework.transaction.config.internalTransactionalEventListenerFactory 来源:class path resource [org/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class]</span><br><span class="line">transactionTemplate 来源:class path resource [org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$TransactionTemplateConfiguration.class]</span><br><span class="line">platformTransactionManagerCustomizers 来源:class path resource [org/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.class]</span><br></pre></td></tr></table></figure>

<p>这些第三方配置给我们添加了很多<code>Bean</code>。</p>
<p>进入<code>DataSourceAutoConfiguration</code>中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123;DataSource.class, EmbeddedDatabaseType.class&#125;)</span></span><br><span class="line"><span class="meta">@ConditionalOnMissingBean(</span></span><br><span class="line"><span class="meta">    type = &#123;&quot;io.r2dbc.spi.ConnectionFactory&quot;&#125;</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="comment">// 绑定环境中以spring.datasource为前缀的键值信息</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(&#123;DataSourceProperties.class&#125;)</span></span><br><span class="line"><span class="meta">@Import(&#123;DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.InitializationSpecificCredentialsDataSourceInitializationConfiguration.class, DataSourceInitializationConfiguration.SharedCredentialsDataSourceInitializationConfiguration.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DataSourceAutoConfiguration</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DataSourceAutoConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EmbeddedDatabaseCondition</span> <span class="keyword">extends</span> <span class="title class_">SpringBootCondition</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">DATASOURCE_URL_PROPERTY</span> <span class="operator">=</span> <span class="string">&quot;spring.datasource.url&quot;</span>;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">SpringBootCondition</span> <span class="variable">pooledCondition</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">PooledDataSourceCondition</span>();</span><br><span class="line"></span><br><span class="line">        EmbeddedDatabaseCondition() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> ConditionOutcome <span class="title function_">getMatchOutcome</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> &#123;</span><br><span class="line">            ConditionMessage.<span class="type">Builder</span> <span class="variable">message</span> <span class="operator">=</span> ConditionMessage.forCondition(<span class="string">&quot;EmbeddedDataSource&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span>.hasDataSourceUrlProperty(context)) &#123;</span><br><span class="line">                <span class="keyword">return</span> ConditionOutcome.noMatch(message.because(<span class="string">&quot;spring.datasource.url is set&quot;</span>));</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">this</span>.anyMatches(context, metadata, <span class="keyword">new</span> <span class="title class_">Condition</span>[]&#123;<span class="built_in">this</span>.pooledCondition&#125;)) &#123;</span><br><span class="line">                <span class="keyword">return</span> ConditionOutcome.noMatch(message.foundExactly(<span class="string">&quot;supported pooled data source&quot;</span>));</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">EmbeddedDatabaseType</span> <span class="variable">type</span> <span class="operator">=</span> EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();</span><br><span class="line">                <span class="keyword">return</span> type == <span class="literal">null</span> ? ConditionOutcome.noMatch(message.didNotFind(<span class="string">&quot;embedded database&quot;</span>).atAll()) : ConditionOutcome.match(message.found(<span class="string">&quot;embedded database&quot;</span>).items(<span class="keyword">new</span> <span class="title class_">Object</span>[]&#123;type&#125;));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> <span class="type">boolean</span> <span class="title function_">hasDataSourceUrlProperty</span><span class="params">(ConditionContext context)</span> &#123;</span><br><span class="line">            <span class="type">Environment</span> <span class="variable">environment</span> <span class="operator">=</span> context.getEnvironment();</span><br><span class="line">            <span class="keyword">if</span> (environment.containsProperty(<span class="string">&quot;spring.datasource.url&quot;</span>)) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">return</span> StringUtils.hasText(environment.getProperty(<span class="string">&quot;spring.datasource.url&quot;</span>));</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IllegalArgumentException var4) &#123;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PooledDataSourceAvailableCondition</span> <span class="keyword">extends</span> <span class="title class_">SpringBootCondition</span> &#123;</span><br><span class="line">        PooledDataSourceAvailableCondition() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> ConditionOutcome <span class="title function_">getMatchOutcome</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> &#123;</span><br><span class="line">            ConditionMessage.<span class="type">Builder</span> <span class="variable">message</span> <span class="operator">=</span> ConditionMessage.forCondition(<span class="string">&quot;PooledDataSource&quot;</span>, <span class="keyword">new</span> <span class="title class_">Object</span>[<span class="number">0</span>]);</span><br><span class="line">            <span class="keyword">return</span> DataSourceBuilder.findType(context.getClassLoader()) != <span class="literal">null</span> ? ConditionOutcome.match(message.foundExactly(<span class="string">&quot;supported DataSource&quot;</span>)) : ConditionOutcome.noMatch(message.didNotFind(<span class="string">&quot;supported DataSource&quot;</span>).atAll());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PooledDataSourceCondition</span> <span class="keyword">extends</span> <span class="title class_">AnyNestedCondition</span> &#123;</span><br><span class="line">        PooledDataSourceCondition() &#123;</span><br><span class="line">            <span class="built_in">super</span>(ConfigurationPhase.PARSE_CONFIGURATION);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Conditional(&#123;PooledDataSourceAvailableCondition.class&#125;)</span></span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PooledDataSourceAvailable</span> &#123;</span><br><span class="line">            PooledDataSourceAvailable() &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@ConditionalOnProperty(</span></span><br><span class="line"><span class="meta">            prefix = &quot;spring.datasource&quot;,</span></span><br><span class="line"><span class="meta">            name = &#123;&quot;type&quot;&#125;</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ExplicitType</span> &#123;</span><br><span class="line">            ExplicitType() &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">     <span class="comment">// 是否支持连接池数据源</span></span><br><span class="line">    <span class="meta">@Conditional(&#123;PooledDataSourceCondition.class&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(&#123;DataSource.class, XADataSource.class&#125;)</span></span><br><span class="line">    <span class="comment">// 导入以下数据源配置</span></span><br><span class="line">    <span class="meta">@Import(&#123;DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class, DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class, DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class&#125;)</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">PooledDataSourceConfiguration</span> &#123;</span><br><span class="line">        <span class="keyword">protected</span> <span class="title function_">PooledDataSourceConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="comment">// 是否支持内嵌数据源</span></span><br><span class="line">    <span class="meta">@Conditional(&#123;EmbeddedDatabaseCondition.class&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(&#123;DataSource.class, XADataSource.class&#125;)</span></span><br><span class="line">    <span class="meta">@Import(&#123;EmbeddedDataSourceConfiguration.class&#125;)</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EmbeddedDatabaseConfiguration</span> &#123;</span><br><span class="line">        <span class="keyword">protected</span> <span class="title function_">EmbeddedDatabaseConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在<code>spring</code>容器创建时候<code>DataSourceProperties</code>会绑定以<code>spring.datasource</code>为前缀的键值信息，我们这里可以打印一下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">DataSourceProperties</span> <span class="variable">dataSourceProperties</span> <span class="operator">=</span> context.getBean(DataSourceProperties.class);</span><br><span class="line">System.out.println(dataSourceProperties.getUrl());</span><br><span class="line">System.out.println(dataSourceProperties.getUsername());</span><br><span class="line">System.out.println(dataSourceProperties.getPassword());</span><br></pre></td></tr></table></figure>

<p>查看输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">jdbc:mysql://localhost:3306/secondhandtradingplatform</span><br><span class="line">root</span><br><span class="line">2001</span><br></pre></td></tr></table></figure>

<p>我们可以查看<code>DataSourceConfiguration.Hikari.class</code>类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Hikari</span> &#123;</span><br><span class="line">    Hikari() &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="meta">@ConfigurationProperties(</span></span><br><span class="line"><span class="meta">        prefix = &quot;spring.datasource.hikari&quot;</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    HikariDataSource <span class="title function_">dataSource</span><span class="params">(DataSourceProperties properties)</span> &#123;</span><br><span class="line">        <span class="type">HikariDataSource</span> <span class="variable">dataSource</span> <span class="operator">=</span> (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class);</span><br><span class="line">        <span class="keyword">if</span> (StringUtils.hasText(properties.getName())) &#123;</span><br><span class="line">            dataSource.setPoolName(properties.getName());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> dataSource;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>dataSource()</code>方法上标注了<code>@Bean</code>注解，参数依赖注入了<code>DataSourceProperties</code>，在<code>createDataSource()</code>方法中会使用到<code>DataSourceProperties</code>。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="keyword">static</span> &lt;T&gt; T <span class="title function_">createDataSource</span><span class="params">(DataSourceProperties properties, Class&lt;? extends DataSource&gt; type)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> properties.initializeDataSourceBuilder().type(type).build();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>DataSourceAutoConfiguration总结：</strong></p>
<ul>
<li>对应的自动配置类为：<code>org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration</code></li>
<li>它内部采用了条件装配，通过检查容器的<code>bean</code>，以及类路径下的<code>class</code>，来决定该<code>@Bean</code>是否生效</li>
</ul>
<p><code>Spring Boot</code>支持两大类数据源：</p>
<ul>
<li><code>EmbeddedDatabase </code>- 内嵌数据库连接池</li>
<li><code>PooledDataSource </code>- 非内嵌数据库连接池</li>
</ul>
<p><code>PooledDataSource</code>又支持如下数据源</p>
<ul>
<li><code>hikari</code>提供的<code>HikariDataSource</code></li>
<li><code>tomcat-jdbc</code>提供的<code>DataSource</code></li>
<li><code>dbcp2</code>提供的<code>BasicDataSource</code></li>
<li><code>oracle</code>提供的<code>PoolDataSourceImpl</code></li>
</ul>
<p>如果知道数据源的实现类类型，即指定了 <code>spring.datasource.type</code>，理论上可以支持所有数据源，但这样做的一个最大问题是无法订制每种数据源的详细配置（如最大、最小连接数等）</p>
<ol start="3">
<li><strong>MybatisAutoConfiguration</strong></li>
</ol>
<p>进入<code>MybatisAutoConfiguration</code>类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="comment">// 必须在类路径下找到SqlSessionFactory类、SqlSessionFactoryBean类</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123;SqlSessionFactory.class, SqlSessionFactoryBean.class&#125;)</span></span><br><span class="line"><span class="comment">// 在类路径下必须找到有且仅有一个的DataSource类型</span></span><br><span class="line"><span class="meta">@ConditionalOnSingleCandidate(DataSource.class)</span></span><br><span class="line"><span class="comment">// 绑定环境中以&quot;mybatis&quot;为前缀的键值信息</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(&#123;MybatisProperties.class&#125;)</span></span><br><span class="line"><span class="comment">// 控制多个配置类的解析顺序，必须在DataSourceAutoConfiguration类与MybatisLanguageDriverAutoConfiguration类解析之后</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123;DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MybatisAutoConfiguration</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Logger</span> <span class="variable">logger</span> <span class="operator">=</span> LoggerFactory.getLogger(MybatisAutoConfiguration.class);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> MybatisProperties properties;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Interceptor[] interceptors;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> TypeHandler[] typeHandlers;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> LanguageDriver[] languageDrivers;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ResourceLoader resourceLoader;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> DatabaseIdProvider databaseIdProvider;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> List&lt;ConfigurationCustomizer&gt; configurationCustomizers;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MybatisAutoConfiguration</span><span class="params">(MybatisProperties properties, ObjectProvider&lt;Interceptor[]&gt; interceptorsProvider, ObjectProvider&lt;TypeHandler[]&gt; typeHandlersProvider, ObjectProvider&lt;LanguageDriver[]&gt; languageDriversProvider, ResourceLoader resourceLoader, ObjectProvider&lt;DatabaseIdProvider&gt; databaseIdProvider, ObjectProvider&lt;List&lt;ConfigurationCustomizer&gt;&gt; configurationCustomizersProvider)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.properties = properties;</span><br><span class="line">        <span class="built_in">this</span>.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();</span><br><span class="line">        <span class="built_in">this</span>.typeHandlers = (TypeHandler[])typeHandlersProvider.getIfAvailable();</span><br><span class="line">        <span class="built_in">this</span>.languageDrivers = (LanguageDriver[])languageDriversProvider.getIfAvailable();</span><br><span class="line">        <span class="built_in">this</span>.resourceLoader = resourceLoader;</span><br><span class="line">        <span class="built_in">this</span>.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();</span><br><span class="line">        <span class="built_in">this</span>.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.checkConfigFileExists();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">checkConfigFileExists</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.properties.isCheckConfigLocation() &amp;&amp; StringUtils.hasText(<span class="built_in">this</span>.properties.getConfigLocation())) &#123;</span><br><span class="line">            <span class="type">Resource</span> <span class="variable">resource</span> <span class="operator">=</span> <span class="built_in">this</span>.resourceLoader.getResource(<span class="built_in">this</span>.properties.getConfigLocation());</span><br><span class="line">            Assert.state(resource.exists(), <span class="string">&quot;Cannot find config location: &quot;</span> + resource + <span class="string">&quot; (please add config file or check your Mybatis configuration)&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="comment">// 当容器中没有SqlSessionFactory类时生效</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionFactory <span class="title function_">sqlSessionFactory</span><span class="params">(DataSource dataSource)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">SqlSessionFactoryBean</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlSessionFactoryBean</span>();</span><br><span class="line">        factory.setDataSource(dataSource);</span><br><span class="line">        factory.setVfs(SpringBootVFS.class);</span><br><span class="line">        <span class="keyword">if</span> (StringUtils.hasText(<span class="built_in">this</span>.properties.getConfigLocation())) &#123;</span><br><span class="line">            factory.setConfigLocation(<span class="built_in">this</span>.resourceLoader.getResource(<span class="built_in">this</span>.properties.getConfigLocation()));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="built_in">this</span>.applyConfiguration(factory);</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.properties.getConfigurationProperties() != <span class="literal">null</span>) &#123;</span><br><span class="line">            factory.setConfigurationProperties(<span class="built_in">this</span>.properties.getConfigurationProperties());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!ObjectUtils.isEmpty(<span class="built_in">this</span>.interceptors)) &#123;</span><br><span class="line">            factory.setPlugins(<span class="built_in">this</span>.interceptors);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.databaseIdProvider != <span class="literal">null</span>) &#123;</span><br><span class="line">            factory.setDatabaseIdProvider(<span class="built_in">this</span>.databaseIdProvider);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (StringUtils.hasLength(<span class="built_in">this</span>.properties.getTypeAliasesPackage())) &#123;</span><br><span class="line">            factory.setTypeAliasesPackage(<span class="built_in">this</span>.properties.getTypeAliasesPackage());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.properties.getTypeAliasesSuperType() != <span class="literal">null</span>) &#123;</span><br><span class="line">            factory.setTypeAliasesSuperType(<span class="built_in">this</span>.properties.getTypeAliasesSuperType());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (StringUtils.hasLength(<span class="built_in">this</span>.properties.getTypeHandlersPackage())) &#123;</span><br><span class="line">            factory.setTypeHandlersPackage(<span class="built_in">this</span>.properties.getTypeHandlersPackage());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!ObjectUtils.isEmpty(<span class="built_in">this</span>.typeHandlers)) &#123;</span><br><span class="line">            factory.setTypeHandlers(<span class="built_in">this</span>.typeHandlers);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (!ObjectUtils.isEmpty(<span class="built_in">this</span>.properties.resolveMapperLocations())) &#123;</span><br><span class="line">            factory.setMapperLocations(<span class="built_in">this</span>.properties.resolveMapperLocations());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        Set&lt;String&gt; factoryPropertyNames = (Set)Stream.of((<span class="keyword">new</span> <span class="title class_">BeanWrapperImpl</span>(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());</span><br><span class="line">        Class&lt;? <span class="keyword">extends</span> <span class="title class_">LanguageDriver</span>&gt; defaultLanguageDriver = <span class="built_in">this</span>.properties.getDefaultScriptingLanguageDriver();</span><br><span class="line">        <span class="keyword">if</span> (factoryPropertyNames.contains(<span class="string">&quot;scriptingLanguageDrivers&quot;</span>) &amp;&amp; !ObjectUtils.isEmpty(<span class="built_in">this</span>.languageDrivers)) &#123;</span><br><span class="line">            factory.setScriptingLanguageDrivers(<span class="built_in">this</span>.languageDrivers);</span><br><span class="line">            <span class="keyword">if</span> (defaultLanguageDriver == <span class="literal">null</span> &amp;&amp; <span class="built_in">this</span>.languageDrivers.length == <span class="number">1</span>) &#123;</span><br><span class="line">                defaultLanguageDriver = <span class="built_in">this</span>.languageDrivers[<span class="number">0</span>].getClass();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (factoryPropertyNames.contains(<span class="string">&quot;defaultScriptingLanguageDriver&quot;</span>)) &#123;</span><br><span class="line">            factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> factory.getObject();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">applyConfiguration</span><span class="params">(SqlSessionFactoryBean factory)</span> &#123;</span><br><span class="line">        org.apache.ibatis.session.<span class="type">Configuration</span> <span class="variable">configuration</span> <span class="operator">=</span> <span class="built_in">this</span>.properties.getConfiguration();</span><br><span class="line">        <span class="keyword">if</span> (configuration == <span class="literal">null</span> &amp;&amp; !StringUtils.hasText(<span class="built_in">this</span>.properties.getConfigLocation())) &#123;</span><br><span class="line">            configuration = <span class="keyword">new</span> <span class="title class_">org</span>.apache.ibatis.session.Configuration();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> (configuration != <span class="literal">null</span> &amp;&amp; !CollectionUtils.isEmpty(<span class="built_in">this</span>.configurationCustomizers)) &#123;</span><br><span class="line">            <span class="type">Iterator</span> <span class="variable">var3</span> <span class="operator">=</span> <span class="built_in">this</span>.configurationCustomizers.iterator();</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span>(var3.hasNext()) &#123;</span><br><span class="line">                <span class="type">ConfigurationCustomizer</span> <span class="variable">customizer</span> <span class="operator">=</span> (ConfigurationCustomizer)var3.next();</span><br><span class="line">                customizer.customize(configuration);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        factory.setConfiguration(configuration);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Bean</span></span><br><span class="line">    <span class="comment">// 提供SqlSessionTemplate Bean</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean</span></span><br><span class="line">    <span class="keyword">public</span> SqlSessionTemplate <span class="title function_">sqlSessionTemplate</span><span class="params">(SqlSessionFactory sqlSessionFactory)</span> &#123;</span><br><span class="line">        <span class="type">ExecutorType</span> <span class="variable">executorType</span> <span class="operator">=</span> <span class="built_in">this</span>.properties.getExecutorType();</span><br><span class="line">        <span class="keyword">return</span> executorType != <span class="literal">null</span> ? <span class="keyword">new</span> <span class="title class_">SqlSessionTemplate</span>(sqlSessionFactory, executorType) : <span class="keyword">new</span> <span class="title class_">SqlSessionTemplate</span>(sqlSessionFactory);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="meta">@Import(&#123;AutoConfiguredMapperScannerRegistrar.class&#125;)</span></span><br><span class="line">    <span class="comment">// 容器中必须缺失MapperFactoryBean类、MapperScannerConfigurer类，此配置才生效</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(&#123;MapperFactoryBean.class, MapperScannerConfigurer.class&#125;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MapperScannerRegistrarNotFoundConfiguration</span> <span class="keyword">implements</span> <span class="title class_">InitializingBean</span> &#123;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">MapperScannerRegistrarNotFoundConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">afterPropertiesSet</span><span class="params">()</span> &#123;</span><br><span class="line">            MybatisAutoConfiguration.logger.debug(<span class="string">&quot;Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguredMapperScannerRegistrar</span> <span class="keyword">implements</span> <span class="title class_">BeanFactoryAware</span>, ImportBeanDefinitionRegistrar &#123;</span><br><span class="line">        <span class="keyword">private</span> BeanFactory beanFactory;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">AutoConfiguredMapperScannerRegistrar</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (!AutoConfigurationPackages.has(<span class="built_in">this</span>.beanFactory)) &#123;</span><br><span class="line">                MybatisAutoConfiguration.logger.debug(<span class="string">&quot;Could not determine auto-configuration package, automatic mapper scanning disabled.&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                MybatisAutoConfiguration.logger.debug(<span class="string">&quot;Searching for mappers annotated with @Mapper&quot;</span>);</span><br><span class="line">                List&lt;String&gt; packages = AutoConfigurationPackages.get(<span class="built_in">this</span>.beanFactory);</span><br><span class="line">                <span class="keyword">if</span> (MybatisAutoConfiguration.logger.isDebugEnabled()) &#123;</span><br><span class="line">                    packages.forEach((pkg) -&gt; &#123;</span><br><span class="line">                        MybatisAutoConfiguration.logger.debug(<span class="string">&quot;Using auto-configuration base package &#x27;&#123;&#125;&#x27;&quot;</span>, pkg);</span><br><span class="line">                    &#125;);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="type">BeanDefinitionBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);</span><br><span class="line">                builder.addPropertyValue(<span class="string">&quot;processPropertyPlaceHolders&quot;</span>, <span class="literal">true</span>);</span><br><span class="line">                builder.addPropertyValue(<span class="string">&quot;annotationClass&quot;</span>, Mapper.class);</span><br><span class="line">                builder.addPropertyValue(<span class="string">&quot;basePackage&quot;</span>, StringUtils.collectionToCommaDelimitedString(packages));</span><br><span class="line">                <span class="type">BeanWrapper</span> <span class="variable">beanWrapper</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BeanWrapperImpl</span>(MapperScannerConfigurer.class);</span><br><span class="line">                Set&lt;String&gt; propertyNames = (Set)Stream.of(beanWrapper.getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());</span><br><span class="line">                <span class="keyword">if</span> (propertyNames.contains(<span class="string">&quot;lazyInitialization&quot;</span>)) &#123;</span><br><span class="line">                    builder.addPropertyValue(<span class="string">&quot;lazyInitialization&quot;</span>, <span class="string">&quot;$&#123;mybatis.lazy-initialization:false&#125;&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">if</span> (propertyNames.contains(<span class="string">&quot;defaultScope&quot;</span>)) &#123;</span><br><span class="line">                    builder.addPropertyValue(<span class="string">&quot;defaultScope&quot;</span>, <span class="string">&quot;$&#123;mybatis.mapper-default-scope:&#125;&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBeanFactory</span><span class="params">(BeanFactory beanFactory)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.beanFactory = beanFactory;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们添加<code>mybatis</code>的包扫描：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">packageName</span> <span class="operator">=</span> TestDataSourceAuto.class.getPackageName();</span><br><span class="line">AutoConfigurationPackages.register(context.getDefaultListableBeanFactory(),</span><br><span class="line">        packageName);</span><br></pre></td></tr></table></figure>

<p>查看控制台输出：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mapper1 来源:file [F:\Java\黑马全套java教程\第2阶段企业级开发—基础框架\7、spring高级45讲\代码\代码\show\target\classes\com\itheima\a41\mapper\Mapper1.class]</span><br><span class="line">mapper2 来源:file [F:\Java\黑马全套java教程\第2阶段企业级开发—基础框架\7、spring高级45讲\代码\代码\show\target\classes\com\itheima\a41\mapper\Mapper2.class]</span><br></pre></td></tr></table></figure>

<p>发现能将我们的自定义mapper加入到容器中。</p>
<p><code>SpringBoot</code>在启动时也会将报名进行注册，查看<code>@SpringBootApplication</code>注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@SpringBootConfiguration</span></span><br><span class="line"><span class="meta">@EnableAutoConfiguration</span></span><br><span class="line"><span class="meta">@ComponentScan(</span></span><br><span class="line"><span class="meta">    excludeFilters = &#123;@Filter(</span></span><br><span class="line"><span class="meta">    type = FilterType.CUSTOM,</span></span><br><span class="line"><span class="meta">    classes = &#123;TypeExcludeFilter.class&#125;</span></span><br><span class="line"><span class="meta">), @Filter(</span></span><br><span class="line"><span class="meta">    type = FilterType.CUSTOM,</span></span><br><span class="line"><span class="meta">    classes = &#123;AutoConfigurationExcludeFilter.class&#125;</span></span><br><span class="line"><span class="meta">)&#125;</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> SpringBootApplication &#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入<code>@EnableAutoConfiguration</code>注解中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@AutoConfigurationPackage</span></span><br><span class="line"><span class="meta">@Import(&#123;AutoConfigurationImportSelector.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAutoConfiguration &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">ENABLED_OVERRIDE_PROPERTY</span> <span class="operator">=</span> <span class="string">&quot;spring.boot.enableautoconfiguration&quot;</span>;</span><br><span class="line"></span><br><span class="line">    Class&lt;?&gt;[] exclude() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    String[] excludeName() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再进入<code>@AutoConfigurationPackage</code>注解：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@Import(&#123;AutoConfigurationPackages.Registrar.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> AutoConfigurationPackage &#123;</span><br><span class="line">    String[] basePackages() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    Class&lt;?&gt;[] basePackageClasses() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再进入<code>AutoConfigurationPackages.Registrar.class</code>中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Registrar</span> <span class="keyword">implements</span> <span class="title class_">ImportBeanDefinitionRegistrar</span>, DeterminableImports &#123;</span><br><span class="line">    Registrar() &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">registerBeanDefinitions</span><span class="params">(AnnotationMetadata metadata, BeanDefinitionRegistry registry)</span> &#123;</span><br><span class="line">        AutoConfigurationPackages.register(registry, (String[])(<span class="keyword">new</span> <span class="title class_">PackageImports</span>(metadata)).getPackageNames().toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> Set&lt;Object&gt; <span class="title function_">determineImports</span><span class="params">(AnnotationMetadata metadata)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> Collections.singleton(<span class="keyword">new</span> <span class="title class_">PackageImports</span>(metadata));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们可以发现在<code>registerBeanDefinitions()</code>方法中注册了包名。</p>
<p><strong>MybatisAutoConfiguration总结：</strong></p>
<ul>
<li>MyBatis 自动配置类为 <code>org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration</code></li>
<li>它主要配置了两个<code>bean</code><ul>
<li><code>SqlSessionFactory</code>：<code>MyBatis</code>核心对象，用来创建<code>SqlSession</code></li>
<li><code>SqlSessionTemplate</code>：<code>SqlSession</code>的实现，此实现会与当前线程绑定</li>
<li>用<code>ImportBeanDefinitionRegistrar</code>的方式扫描所有标注了<code>@Mapper</code>注解的接口</li>
<li>用<code>AutoConfigurationPackages</code>来确定扫描的包</li>
</ul>
</li>
<li>还有一个相关的<code>bean</code>：<code>MybatisProperties</code>，它会读取配置文件中带 <code>mybatis.</code> 前缀的配置项进行定制配置</li>
</ul>
<p><code>@MapperScan</code>注解的作用与<code>MybatisAutoConfiguration</code>类似，和<code>MapperScannerConfigurer</code>有如下区别：</p>
<ul>
<li><code>@MapperScan</code>扫描具体包（当然也可以配置关注哪个注解）</li>
<li><code>@MapperScan</code>如果不指定扫描具体包，则会把引导类范围内，所有接口当做Mapper接口</li>
<li><code>MybatisAutoConfiguration</code>关注的是所有标注<code>@Mapper</code>注解的接口，会忽略掉非<code>@Mapper</code>标注的接口</li>
</ul>
<ol start="4">
<li><strong>DataSourceTransactionManagerAutoConfiguration</strong></li>
</ol>
<p><code>TransactionManager</code>是事务管理器，其作用是去执行底层事务的相关方法。</p>
<p>进入<code>DataSourceTransactionManagerAutoConfiguration</code>类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123;JdbcTemplate.class, TransactionManager.class&#125;)</span></span><br><span class="line"><span class="meta">@AutoConfigureOrder(Integer.MAX_VALUE)</span></span><br><span class="line"><span class="meta">@EnableConfigurationProperties(&#123;DataSourceProperties.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DataSourceTransactionManagerAutoConfiguration</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DataSourceTransactionManagerAutoConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="meta">@ConditionalOnSingleCandidate(DataSource.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">JdbcTransactionManagerConfiguration</span> &#123;</span><br><span class="line">        JdbcTransactionManagerConfiguration() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="comment">// 当缺失了TransactionManager才进行注入</span></span><br><span class="line">        <span class="meta">@ConditionalOnMissingBean(&#123;TransactionManager.class&#125;)</span></span><br><span class="line">        DataSourceTransactionManager <span class="title function_">transactionManager</span><span class="params">(Environment environment, DataSource dataSource, ObjectProvider&lt;TransactionManagerCustomizers&gt; transactionManagerCustomizers)</span> &#123;</span><br><span class="line">            <span class="type">DataSourceTransactionManager</span> <span class="variable">transactionManager</span> <span class="operator">=</span> <span class="built_in">this</span>.createTransactionManager(environment, dataSource);</span><br><span class="line">            transactionManagerCustomizers.ifAvailable((customizers) -&gt; &#123;</span><br><span class="line">                customizers.customize(transactionManager);</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="keyword">return</span> transactionManager;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">private</span> DataSourceTransactionManager <span class="title function_">createTransactionManager</span><span class="params">(Environment environment, DataSource dataSource)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> (DataSourceTransactionManager)((Boolean)environment.getProperty(<span class="string">&quot;spring.dao.exceptiontranslation.enabled&quot;</span>, Boolean.class, Boolean.TRUE) ? <span class="keyword">new</span> <span class="title class_">JdbcTransactionManager</span>(dataSource) : <span class="keyword">new</span> <span class="title class_">DataSourceTransactionManager</span>(dataSource));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ol start="5">
<li><strong>TransactionAutoConfiguration</strong></li>
</ol>
<ul>
<li>事务自动配置类有两个：<ul>
<li><code>org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration</code></li>
<li><code>org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration</code></li>
</ul>
</li>
<li>前者配置了<code>DataSourceTransactionManager</code>用来执行事务的提交、回滚操作</li>
<li>后者功能上对标<code>@EnableTransactionManagement</code>，包含以下三个<code>bean</code><ul>
<li><code>BeanFactoryTransactionAttributeSourceAdvisor</code>事务切面类，包含通知和切点</li>
<li><code>TransactionInterceptor</code>事务通知类，由它在目标方法调用前后加入事务操作</li>
<li><code>AnnotationTransactionAttributeSource</code>会解析<code>@Transactional</code>及事务属性，也包含了切点功能</li>
</ul>
</li>
<li>如果自己配置了<code>DataSourceTransactionManager</code>或是在引导类加了<code>@EnableTransactionManagement</code>，则以自己配置的为准</li>
</ul>
<ol start="6">
<li><strong>ServletWebServerFactoryAutoConfiguration</strong></li>
</ol>
<p>编写测试代码，添加<code>ServletWebServerFactoryAutoConfiguration</code>、<code>DispatcherServletAutoConfiguration</code>、<code>WebMvcAutoConfiguration</code>、<code>ErrorMvcAutoConfiguration</code>等第三方配置</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestMvcAuto</span> &#123;</span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">AnnotationConfigServletWebServerApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigServletWebServerApplicationContext</span>();</span><br><span class="line">        context.registerBean(Config.class);</span><br><span class="line">        context.refresh();</span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">source</span> <span class="operator">=</span> context.getBeanDefinition(name).getResourceDescription();</span><br><span class="line">            <span class="keyword">if</span> (source != <span class="literal">null</span>) &#123;</span><br><span class="line">                System.out.println(name + <span class="string">&quot; 来源:&quot;</span> + source);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span></span><br><span class="line">    <span class="meta">@Import(MyImportSelector.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;</span><br><span class="line">                    ServletWebServerFactoryAutoConfiguration.class.getName(),</span><br><span class="line">                    DispatcherServletAutoConfiguration.class.getName(),</span><br><span class="line">                    WebMvcAutoConfiguration.class.getName(),</span><br><span class="line">                    ErrorMvcAutoConfiguration.class.getName()</span><br><span class="line">            &#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line">tomcatServletWebServerFactory 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration$EmbeddedTomcat.class]</span><br><span class="line">servletWebServerFactoryCustomizer 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.class]</span><br><span class="line">tomcatServletWebServerFactoryCustomizer 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.class]</span><br><span class="line">dispatcherServlet 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletConfiguration.class]</span><br><span class="line">dispatcherServletRegistration 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration.class]</span><br><span class="line">requestMappingHandlerAdapter 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">requestMappingHandlerMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">welcomePageHandlerMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">localeResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">themeResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">flashMapManager 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcConversionService 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcValidator 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcContentNegotiationManager 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcPatternParser 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcUrlPathHelper 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcPathMatcher 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">viewControllerHandlerMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">beanNameHandlerMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">routerFunctionMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">resourceHandlerMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcResourceUrlProvider 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">defaultServletHandlerMapping 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">handlerFunctionAdapter 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcUriComponentsContributor 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">httpRequestHandlerAdapter 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">simpleControllerHandlerAdapter 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">handlerExceptionResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcViewResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">mvcHandlerMappingIntrospector 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">viewNameTranslator 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]</span><br><span class="line">defaultViewResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]</span><br><span class="line">viewResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]</span><br><span class="line">requestContextFilter 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]</span><br><span class="line">formContentFilter 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.class]</span><br><span class="line">error 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]</span><br><span class="line">beanNameViewResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]</span><br><span class="line">conventionErrorViewResolver 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration.class]</span><br><span class="line">errorAttributes 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]</span><br><span class="line">basicErrorController 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]</span><br><span class="line">errorPageCustomizer 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]</span><br><span class="line">preserveErrorControllerTargetClassPostProcessor 来源:class path resource [org/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class]</span><br><span class="line">tomcatServletWebServerFactory`来源为`class path resource [org/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration$EmbeddedTomcat.class]</span><br></pre></td></tr></table></figure>

<p>进入<code>ServletWebServerFactoryConfiguration</code>，其中支持多种内嵌服务器：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ServletWebServerFactoryConfiguration</span> &#123;</span><br><span class="line">    ServletWebServerFactoryConfiguration() &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(&#123;Servlet.class, Undertow.class, SslClientAuthMode.class&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(</span></span><br><span class="line"><span class="meta">        value = &#123;ServletWebServerFactory.class&#125;,</span></span><br><span class="line"><span class="meta">        search = SearchStrategy.CURRENT</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EmbeddedUndertow</span> &#123;</span><br><span class="line">        EmbeddedUndertow() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        UndertowServletWebServerFactory <span class="title function_">undertowServletWebServerFactory</span><span class="params">(ObjectProvider&lt;UndertowDeploymentInfoCustomizer&gt; deploymentInfoCustomizers, ObjectProvider&lt;UndertowBuilderCustomizer&gt; builderCustomizers)</span> &#123;</span><br><span class="line">            <span class="type">UndertowServletWebServerFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UndertowServletWebServerFactory</span>();</span><br><span class="line">            factory.getDeploymentInfoCustomizers().addAll((Collection)deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));</span><br><span class="line">            factory.getBuilderCustomizers().addAll((Collection)builderCustomizers.orderedStream().collect(Collectors.toList()));</span><br><span class="line">            <span class="keyword">return</span> factory;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        UndertowServletWebServerFactoryCustomizer <span class="title function_">undertowServletWebServerFactoryCustomizer</span><span class="params">(ServerProperties serverProperties)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">UndertowServletWebServerFactoryCustomizer</span>(serverProperties);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(&#123;Servlet.class, Server.class, Loader.class, WebAppContext.class&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(</span></span><br><span class="line"><span class="meta">        value = &#123;ServletWebServerFactory.class&#125;,</span></span><br><span class="line"><span class="meta">        search = SearchStrategy.CURRENT</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EmbeddedJetty</span> &#123;</span><br><span class="line">        EmbeddedJetty() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        JettyServletWebServerFactory <span class="title function_">JettyServletWebServerFactory</span><span class="params">(ObjectProvider&lt;JettyServerCustomizer&gt; serverCustomizers)</span> &#123;</span><br><span class="line">            <span class="type">JettyServletWebServerFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JettyServletWebServerFactory</span>();</span><br><span class="line">            factory.getServerCustomizers().addAll((Collection)serverCustomizers.orderedStream().collect(Collectors.toList()));</span><br><span class="line">            <span class="keyword">return</span> factory;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(&#123;Servlet.class, Tomcat.class, UpgradeProtocol.class&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnMissingBean(</span></span><br><span class="line"><span class="meta">        value = &#123;ServletWebServerFactory.class&#125;,</span></span><br><span class="line"><span class="meta">        search = SearchStrategy.CURRENT</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">EmbeddedTomcat</span> &#123;</span><br><span class="line">        EmbeddedTomcat() &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        TomcatServletWebServerFactory <span class="title function_">tomcatServletWebServerFactory</span><span class="params">(ObjectProvider&lt;TomcatConnectorCustomizer&gt; connectorCustomizers, ObjectProvider&lt;TomcatContextCustomizer&gt; contextCustomizers, ObjectProvider&lt;TomcatProtocolHandlerCustomizer&lt;?&gt;&gt; protocolHandlerCustomizers)</span> &#123;</span><br><span class="line">            <span class="type">TomcatServletWebServerFactory</span> <span class="variable">factory</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">            factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));</span><br><span class="line">            factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));</span><br><span class="line">            factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));</span><br><span class="line">            <span class="keyword">return</span> factory;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>ServletWebServerFactoryAutoConfiguration总结：</strong></p>
<ul>
<li>提供<code>ServletWebServerFactory</code></li>
</ul>
<ol start="7">
<li><strong>DispatcherServletAutoConfiguration</strong></li>
</ol>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dispatcherServlet`来源为`class path resource [org/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletConfiguration.class]</span><br></pre></td></tr></table></figure>

<p>我们进入<code>DispatcherServletConfiguration</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AutoConfigureOrder(Integer.MIN_VALUE)</span></span><br><span class="line"><span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">    proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@ConditionalOnWebApplication(</span></span><br><span class="line"><span class="meta">    type = Type.SERVLET</span></span><br><span class="line"><span class="meta">)</span></span><br><span class="line"><span class="meta">@ConditionalOnClass(&#123;DispatcherServlet.class&#125;)</span></span><br><span class="line"><span class="meta">@AutoConfigureAfter(&#123;ServletWebServerFactoryAutoConfiguration.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">DispatcherServletAutoConfiguration</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">    </span><br><span class="line">    <span class="meta">@Configuration(</span></span><br><span class="line"><span class="meta">        proxyBeanMethods = false</span></span><br><span class="line"><span class="meta">    )</span></span><br><span class="line">    <span class="meta">@Conditional(&#123;DefaultDispatcherServletCondition.class&#125;)</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(&#123;ServletRegistration.class&#125;)</span></span><br><span class="line">    <span class="meta">@EnableConfigurationProperties(&#123;WebMvcProperties.class&#125;)</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">DispatcherServletConfiguration</span> &#123;</span><br><span class="line">        <span class="keyword">protected</span> <span class="title function_">DispatcherServletConfiguration</span><span class="params">()</span> &#123;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean(</span></span><br><span class="line"><span class="meta">            name = &#123;&quot;dispatcherServlet&quot;&#125;</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="keyword">public</span> DispatcherServlet <span class="title function_">dispatcherServlet</span><span class="params">(WebMvcProperties webMvcProperties)</span> &#123;</span><br><span class="line">            <span class="type">DispatcherServlet</span> <span class="variable">dispatcherServlet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">DispatcherServlet</span>();</span><br><span class="line">            dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());</span><br><span class="line">            dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());</span><br><span class="line">            dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());</span><br><span class="line">            dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());</span><br><span class="line">            dispatcherServlet.setEnableLoggingRequestDetails(webMvcProperties.isLogRequestDetails());</span><br><span class="line">            <span class="keyword">return</span> dispatcherServlet;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="meta">@ConditionalOnBean(&#123;MultipartResolver.class&#125;)</span></span><br><span class="line">        <span class="meta">@ConditionalOnMissingBean(</span></span><br><span class="line"><span class="meta">            name = &#123;&quot;multipartResolver&quot;&#125;</span></span><br><span class="line"><span class="meta">        )</span></span><br><span class="line">        <span class="keyword">public</span> MultipartResolver <span class="title function_">multipartResolver</span><span class="params">(MultipartResolver resolver)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> resolver;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>DispatcherServletAutoConfiguration总结：</strong></p>
<ul>
<li>提供<code>DispatcherServlet</code></li>
<li>提供<code>DispatcherServletRegistrationBean</code></li>
</ul>
<ol start="8">
<li><strong>WebMvcAutoConfiguration</strong></li>
</ol>
<p><strong>WebMvcAutoConfiguration总结：</strong></p>
<ul>
<li>配置<code>DispatcherServlet</code>的各项组件，提供的<code>bean</code>见过的有<ul>
<li>多项<code>HandlerMapping</code></li>
<li>多项<code>HandlerAdapter</code></li>
<li><code>HandlerExceptionResolver</code></li>
</ul>
</li>
</ul>
<ol start="9">
<li><strong>ErrorMvcAutoConfiguration</strong></li>
</ol>
<p><strong>ErrorMvcAutoConfiguration总结：</strong></p>
<ul>
<li>提供的<code>bean</code>有<code>BasicErrorController</code></li>
</ul>
<ol start="10">
<li><strong>MultipartAutoConfiguration</strong></li>
</ol>
<p><strong>MultipartAutoConfiguration总结：</strong></p>
<ul>
<li>它提供了<code>org.springframework.web.multipart.support.StandardServletMultipartResolver</code></li>
<li>该<code>bean</code>用来解析<code>multipart/form-data</code>格式的数据</li>
</ul>
<ol start="11">
<li><strong>HttpEncodingAutoConfiguration</strong></li>
</ol>
<ul>
<li><code>POST</code>请求参数如果有中文，无需特殊设置，这是因为<code>Spring Boot</code>已经配置了 <code>org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter</code></li>
<li>对应配置<code>server.servlet.encoding.charset=UTF-8</code>，默认就是<code>UTF-8</code></li>
<li>当然，它只影响非<code>json</code>格式的数据</li>
</ul>
<h2 id="自定义自动配置类"><a href="#自定义自动配置类" class="headerlink" title="自定义自动配置类"></a>自定义自动配置类</h2><p><code>springboot</code>的<code>EnableAutoConfiguration</code>原理也是基于<code>@Import</code>注解，只是读取的类型不一样，我们进入</p>
<p><code>EnableAutoConfiguration</code>中：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE&#125;)</span></span><br><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Inherited</span></span><br><span class="line"><span class="meta">@AutoConfigurationPackage</span></span><br><span class="line"><span class="meta">@Import(&#123;AutoConfigurationImportSelector.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAutoConfiguration &#123;</span><br><span class="line">    <span class="type">String</span> <span class="variable">ENABLED_OVERRIDE_PROPERTY</span> <span class="operator">=</span> <span class="string">&quot;spring.boot.enableautoconfiguration&quot;</span>;</span><br><span class="line"></span><br><span class="line">    Class&lt;?&gt;[] exclude() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    String[] excludeName() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入<code>@Import()</code>注解中的类<code>AutoConfigurationImportSelector</code>：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AutoConfigurationImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span>, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String[] selectImports(AnnotationMetadata annotationMetadata) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="built_in">this</span>.isEnabled(annotationMetadata)) &#123;</span><br><span class="line">            <span class="keyword">return</span> NO_IMPORTS;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="type">AutoConfigurationEntry</span> <span class="variable">autoConfigurationEntry</span> <span class="operator">=</span> <span class="built_in">this</span>.getAutoConfigurationEntry(annotationMetadata);</span><br><span class="line">            <span class="keyword">return</span> StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>进入<code>getAutoConfigurationEntry()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> AutoConfigurationEntry <span class="title function_">getAutoConfigurationEntry</span><span class="params">(AnnotationMetadata annotationMetadata)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (!<span class="built_in">this</span>.isEnabled(annotationMetadata)) &#123;</span><br><span class="line">        <span class="keyword">return</span> EMPTY_ENTRY;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">AnnotationAttributes</span> <span class="variable">attributes</span> <span class="operator">=</span> <span class="built_in">this</span>.getAttributes(annotationMetadata);</span><br><span class="line">        List&lt;String&gt; configurations = <span class="built_in">this</span>.getCandidateConfigurations(annotationMetadata, attributes);</span><br><span class="line">        configurations = <span class="built_in">this</span>.removeDuplicates(configurations);</span><br><span class="line">        Set&lt;String&gt; exclusions = <span class="built_in">this</span>.getExclusions(annotationMetadata, attributes);</span><br><span class="line">        <span class="built_in">this</span>.checkExcludedClasses(configurations, exclusions);</span><br><span class="line">        configurations.removeAll(exclusions);</span><br><span class="line">        configurations = <span class="built_in">this</span>.getConfigurationClassFilter().filter(configurations);</span><br><span class="line">        <span class="built_in">this</span>.fireAutoConfigurationImportEvents(configurations, exclusions);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">AutoConfigurationEntry</span>(configurations, exclusions);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>再进入<code>getCandidateConfigurations()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> List&lt;String&gt; <span class="title function_">getCandidateConfigurations</span><span class="params">(AnnotationMetadata metadata, AnnotationAttributes attributes)</span> &#123;</span><br><span class="line">    List&lt;String&gt; configurations = SpringFactoriesLoader.loadFactoryNames(<span class="built_in">this</span>.getSpringFactoriesLoaderFactoryClass(), <span class="built_in">this</span>.getBeanClassLoader());</span><br><span class="line">    Assert.notEmpty(configurations, <span class="string">&quot;No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> configurations;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这个方法调用了<code>SpringFactoriesLoader</code>的<code>loadFactoryNames()</code>方法，第一个参数为加载类型，我们进入<code>getSpringFactoriesLoaderFactoryClass()</code>方法：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> Class&lt;?&gt; getSpringFactoriesLoaderFactoryClass() &#123;</span><br><span class="line">    <span class="keyword">return</span> EnableAutoConfiguration.class;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>返回的是一个<code>EnableAutoConfiguration</code>类型。</p>
<p>因此我们只需要在配置中的键更改为<code>org.springframework.boot.autoconfigure.EnableAutoConfiguration</code>即可：</p>
<figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">org.springframework.boot.autoconfigure.EnableAutoConfiguration</span>=<span class="string">\</span></span><br><span class="line"><span class="string">com.itheima.a41.A41_2.AutoConfiguration1,\</span></span><br><span class="line"><span class="string">com.itheima.a41.A41_2.AutoConfiguration2</span></span><br></pre></td></tr></table></figure>

<p>再使用<code>@EnableAutoConfiguration</code>即可。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A41_2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">AnnotationConfigServletWebServerApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AnnotationConfigServletWebServerApplicationContext</span>();</span><br><span class="line">        <span class="type">StandardEnvironment</span> <span class="variable">env</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StandardEnvironment</span>();</span><br><span class="line">        env.getPropertySources().addLast(<span class="keyword">new</span> <span class="title class_">SimpleCommandLinePropertySource</span>(</span><br><span class="line">                <span class="string">&quot;--spring.datasource.url=jdbc:mysql://localhost:3306/secondhandtradingplatform&quot;</span>,</span><br><span class="line">                <span class="string">&quot;--spring.datasource.username=root&quot;</span>,</span><br><span class="line">                <span class="string">&quot;--spring.datasource.password=2001&quot;</span></span><br><span class="line">        ));</span><br><span class="line">        context.setEnvironment(env);</span><br><span class="line">        context.registerBean(<span class="string">&quot;config&quot;</span>, Config.class);</span><br><span class="line">        context.refresh();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">resourceDescription</span> <span class="operator">=</span> context.getBeanDefinition(name).getResourceDescription();</span><br><span class="line">            <span class="keyword">if</span> (resourceDescription != <span class="literal">null</span>)</span><br><span class="line">                System.out.println(name + <span class="string">&quot; 来源:&quot;</span> + resourceDescription);</span><br><span class="line">        &#125;</span><br><span class="line">        context.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 本项目的配置类</span></span><br><span class="line">    <span class="meta">@EnableAutoConfiguration</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> TomcatServletWebServerFactory <span class="title function_">tomcatServletWebServerFactory</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TomcatServletWebServerFactory</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">            <span class="keyword">return</span> SpringFactoriesLoader.loadFactoryNames(MyImportSelector.class, <span class="literal">null</span>).toArray(<span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">0</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration1</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration2</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>运行启动类，查看控制台：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">bean1 来源:class path resource [com/itheima/a41/A41_2$AutoConfiguration1.class]</span><br><span class="line">bean2 来源:class path resource [com/itheima/a41/A41_2$AutoConfiguration2.class]</span><br></pre></td></tr></table></figure>

<p>第三方的配置也已经生效。</p>
<h1 id="条件装配底层"><a href="#条件装配底层" class="headerlink" title="条件装配底层"></a><strong>条件装配底层</strong></h1><p><code>@Conditional()</code>注解是<code>spring</code>提供的注入<code>Bean</code>时的条件，我们来看看它的原理。<code>@Conditional()</code>注解本身没有判断的逻辑，判断逻辑是实现了<code>Condition</code>接口的类。</p>
<p>我们自定义一个实现了<code>Condition</code>接口的类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A42_1</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericApplicationContext</span>();</span><br><span class="line">        context.registerBean(<span class="string">&quot;config&quot;</span>, Config.class);</span><br><span class="line">        context.registerBean(ConfigurationClassPostProcessor.class);</span><br><span class="line">        context.refresh();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 本项目的配置类</span></span><br><span class="line">    <span class="meta">@Import(MyImportSelector.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()&#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyCondition1</span> <span class="keyword">implements</span> <span class="title class_">Condition</span> &#123; <span class="comment">// 存在 Druid 依赖</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">matches</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> ClassUtils.isPresent(<span class="string">&quot;com.alibaba.druid.pool.DruidDataSource&quot;</span>, <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyCondition2</span> <span class="keyword">implements</span> <span class="title class_">Condition</span> &#123; <span class="comment">// 不存在 Druid 依赖</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">matches</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> !ClassUtils.isPresent(<span class="string">&quot;com.alibaba.druid.pool.DruidDataSource&quot;</span>, <span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="meta">@Conditional(MyCondition1.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration1</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="meta">@Conditional(MyCondition2.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration2</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>MyCondition1</code>的逻辑是只有<code>com.alibaba.druid.pool.DruidDataSource</code>存在才注入<code>Bean</code>，<code>MyCondition2</code>的逻辑是只有<code>com.alibaba.druid.pool.DruidDataSource</code>不存在才注入<code>Bean</code>。</p>
<p>以上代码有几个缺点：</p>
<ol>
<li>判断的类型写死在了代码中，我们更希望能作为参数传入</li>
<li>相似的逻辑写了两套，我们希望能统一成一套</li>
</ol>
<p>其实<code>Spring</code>中的条件注解例如<code>@ConditionalOnProperty</code>组合了<code>@Conditional()</code>注解，实现了一些扩展功能。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line"><span class="meta">@Target(&#123;ElementType.TYPE, ElementType.METHOD&#125;)</span></span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Conditional(&#123;OnPropertyCondition.class&#125;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> ConditionalOnProperty &#123;</span><br><span class="line">    String[] value() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    String <span class="title function_">prefix</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    String[] name() <span class="keyword">default</span> &#123;&#125;;</span><br><span class="line"></span><br><span class="line">    String <span class="title function_">havingValue</span><span class="params">()</span> <span class="keyword">default</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line"></span><br><span class="line">    <span class="type">boolean</span> <span class="title function_">matchIfMissing</span><span class="params">()</span> <span class="keyword">default</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们可以自定义注解组合<code>@Conditional()</code>注解更优雅地实现：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">A42_2</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@SuppressWarnings(&quot;all&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line">        <span class="type">GenericApplicationContext</span> <span class="variable">context</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericApplicationContext</span>();</span><br><span class="line">        context.registerBean(<span class="string">&quot;config&quot;</span>, Config.class);</span><br><span class="line">        context.registerBean(ConfigurationClassPostProcessor.class);</span><br><span class="line">        context.refresh();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (String name : context.getBeanDefinitionNames()) &#123;</span><br><span class="line">            System.out.println(name);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 本项目的配置类</span></span><br><span class="line">    <span class="meta">@Import(MyImportSelector.class)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Config</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyImportSelector</span> <span class="keyword">implements</span> <span class="title class_">DeferredImportSelector</span> &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> String[] selectImports(AnnotationMetadata importingClassMetadata) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">String</span>[]&#123;AutoConfiguration1.class.getName(), AutoConfiguration2.class.getName()&#125;;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">MyCondition</span> <span class="keyword">implements</span> <span class="title class_">Condition</span> &#123; <span class="comment">// 存在 Druid 依赖</span></span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">matches</span><span class="params">(ConditionContext context, AnnotatedTypeMetadata metadata)</span> &#123;</span><br><span class="line">            Map&lt;String, Object&gt; attributes = metadata.getAnnotationAttributes(ConditionalOnClass.class.getName());</span><br><span class="line">            <span class="type">String</span> <span class="variable">className</span> <span class="operator">=</span> attributes.get(<span class="string">&quot;className&quot;</span>).toString();</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">exists</span> <span class="operator">=</span> (<span class="type">boolean</span>) attributes.get(<span class="string">&quot;exists&quot;</span>);</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">present</span> <span class="operator">=</span> ClassUtils.isPresent(className, <span class="literal">null</span>);</span><br><span class="line">            <span class="keyword">return</span> exists ? present : !present;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Retention(RetentionPolicy.RUNTIME)</span></span><br><span class="line">    <span class="meta">@Target(&#123;ElementType.METHOD, ElementType.TYPE&#125;)</span></span><br><span class="line">    <span class="meta">@Conditional(MyCondition.class)</span></span><br><span class="line">    <span class="meta">@interface</span> ConditionalOnClass &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="title function_">exists</span><span class="params">()</span>; <span class="comment">// true 判断存在 false 判断不存在</span></span><br><span class="line"></span><br><span class="line">        String <span class="title function_">className</span><span class="params">()</span>; <span class="comment">// 要判断的类名</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(className = &quot;com.alibaba.druid.pool.DruidDataSource&quot;, exists = false)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration1</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean1 <span class="title function_">bean1</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean1</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Configuration</span> <span class="comment">// 第三方的配置类</span></span><br><span class="line">    <span class="meta">@ConditionalOnClass(className = &quot;com.alibaba.druid.pool.DruidDataSource&quot;, exists = true)</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">AutoConfiguration2</span> &#123;</span><br><span class="line">        <span class="meta">@Bean</span></span><br><span class="line">        <span class="keyword">public</span> Bean2 <span class="title function_">bean2</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Bean2</span>();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean1</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Bean2</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上代码就是通过注解传参地方式优雅地实现了条件判断。</p>
</article><div class="post-copyright"><div class="post-copyright__author"><span class="post-copyright-meta">文章作者: </span><span class="post-copyright-info"><a href>狼族少年、血狼</a></span></div><div class="post-copyright__type"><span class="post-copyright-meta">文章链接: </span><span class="post-copyright-info"><a href="https://geekwolfman.github.io/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT.html">https://geekwolfman.github.io/2023/04/15/Spring高级45讲【第五章】：BOOT.html</a></span></div><div class="post-copyright__notice"><span class="post-copyright-meta">版权声明: </span><span class="post-copyright-info">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">CC BY-NC-SA 4.0</a> 许可协议。转载请注明来自 <a href="https://geekwolfman.github.io" target="_blank">狼族少年、血狼</a>！</span></div></div><div class="tag_share"><div class="post-meta__tag-list"><a class="post-meta__tags" href="/tags/spring/">spring</a></div><div class="post_share"><div class="social-share" data-image="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%94%E7%AB%A0%E3%80%91%EF%BC%9ABOOT/01cover.jpg" data-sites="facebook,twitter,wechat,weibo,qq"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/css/share.min.css" media="print" onload="this.media='all'"><script src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/sharejs/dist/js/social-share.min.js" defer></script></div></div><nav class="pagination-post" id="pagination"><div class="prev-post pull-left"><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%85%AD%E7%AB%A0%E3%80%91%EF%BC%9AOTHER.html" title="Spring高级45讲【第六章】：OTHER"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%85%AD%E7%AB%A0%E3%80%91%EF%BC%9AOTHER/00cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of previous post"><div class="pagination-info"><div class="label">上一篇</div><div class="prev_info">Spring高级45讲【第六章】：OTHER</div></div></a></div><div class="next-post pull-right"><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%9B%9B%E7%AB%A0%E3%80%91%EF%BC%9AWEB.html" title="Spring高级45讲【第四章】：WEB"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%9B%9B%E7%AB%A0%E3%80%91%EF%BC%9AWEB/01cover.jpg" onerror="onerror=null;src='/img/404.jpg'" alt="cover of next post"><div class="pagination-info"><div class="label">下一篇</div><div class="next_info">Spring高级45讲【第四章】：WEB</div></div></a></div></nav><div class="relatedPosts"><div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>相关推荐</span></div><div class="relatedPosts-list"><div><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%85%AD%E7%AB%A0%E3%80%91%EF%BC%9AOTHER.html" title="Spring高级45讲【第六章】：OTHER"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%85%AD%E7%AB%A0%E3%80%91%EF%BC%9AOTHER/00cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-04-15</div><div class="title">Spring高级45讲【第六章】：OTHER</div></div></a></div><div><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%9B%9B%E7%AB%A0%E3%80%91%EF%BC%9AWEB.html" title="Spring高级45讲【第四章】：WEB"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E5%9B%9B%E7%AB%A0%E3%80%91%EF%BC%9AWEB/01cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-04-15</div><div class="title">Spring高级45讲【第四章】：WEB</div></div></a></div><div><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%B8%89%E7%AB%A0%E3%80%91%EF%BC%9AAOP.html" title="Spring高级45讲【第三章】：AOP"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%B8%89%E7%AB%A0%E3%80%91%EF%BC%9AAOP/01cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-04-15</div><div class="title">Spring高级45讲【第三章】：AOP</div></div></a></div><div><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%8C%E7%AB%A0%E3%80%91%EF%BC%9AIOC.html" title="Spring高级45讲【第二章】：IOC"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%BA%8C%E7%AB%A0%E3%80%91%EF%BC%9AIOC/01cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-04-15</div><div class="title">Spring高级45讲【第二章】：IOC</div></div></a></div><div><a href="/2023/04/15/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%91%EF%BC%9A%E5%AF%BC%E5%AD%A6.html" title="Spring高级45讲【第一章】：导学"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/Spring%E9%AB%98%E7%BA%A745%E8%AE%B2%E3%80%90%E7%AC%AC%E4%B8%80%E7%AB%A0%E3%80%91%EF%BC%9A%E5%AF%BC%E5%AD%A6/01cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-04-15</div><div class="title">Spring高级45讲【第一章】：导学</div></div></a></div><div><a href="/2023/03/23/%E6%89%8B%E5%86%99%E7%AE%80%E6%98%93%E7%89%88spring-web%E6%A1%86%E6%9E%B6.html" title="手写简易版spring web框架"><img class="cover" src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/articles/%E6%89%8B%E5%86%99%E7%AE%80%E6%98%93%E7%89%88spring-web%E6%A1%86%E6%9E%B6/01cover.jpg" alt="cover"><div class="content is-center"><div class="date"><i class="far fa-calendar-alt fa-fw"></i> 2023-03-23</div><div class="title">手写简易版spring web框架</div></div></a></div></div></div></div><div class="aside-content" id="aside-content"><div class="card-widget card-info"><div class="is-center"><div class="avatar-img"><img src="https://geekwolfman-blog.oss-cn-chengdu.aliyuncs.com/config/avatar/avatar.png" onerror="this.onerror=null;this.src='/img/friend_404.gif'" alt="avatar"/></div><div class="author-info__name">狼族少年、血狼</div><div class="author-info__description"></div></div><div class="card-info-data site-data is-center"><a href="/archives/"><div class="headline">文章</div><div class="length-num">57</div></a><a href="/tags/"><div class="headline">标签</div><div class="length-num">14</div></a><a href="/categories/"><div class="headline">分类</div><div class="length-num">9</div></a></div><a id="card-info-btn" target="_blank" rel="noopener" href="https://wpa.qq.com/msgrd?v=3&amp;uin=2370032534&amp;site=qq&amp;menu=yes&amp;jumpflag=1"><i class="fa-brands fa-qq"></i><span>添加博主QQ</span></a></div><div class="card-widget card-announcement"><div class="item-headline"><i class="fas fa-bullhorn fa-shake"></i><span>公告</span></div><div class="announcement_content">本站所有博文均是博主的学习笔记与个人理解，来源于网络，如有<span style="color:red;font-weight:bold;">侵权</span>请<a target="_blank" rel="noopener" href="https://wpa.qq.com/msgrd?v=3&uin=2370032534&site=qq&menu=yes&jumpflag=1" style="color:#49B1F5;font-weight:bold">联系我</a>进行删除🥰。</div></div><div class="sticky_layout"><div class="card-widget" id="card-toc"><div class="item-headline"><i class="fas fa-stream"></i><span>目录</span><span class="toc-percentage"></span></div><div class="toc-content"><ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%9E%84%E5%BB%BABoot%E9%A1%B9%E7%9B%AE"><span class="toc-number">1.</span> <span class="toc-text">构建Boot项目</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E4%BC%A0%E7%BB%9F%E6%96%B9%E5%BC%8F%E7%94%9F%E6%88%90"><span class="toc-number">1.1.</span> <span class="toc-text">传统方式生成</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BF%AB%E6%8D%B7%E6%96%B9%E5%BC%8F%E7%94%9F%E6%88%90"><span class="toc-number">1.2.</span> <span class="toc-text">快捷方式生成</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Boot-War%E9%A1%B9%E7%9B%AE"><span class="toc-number">2.</span> <span class="toc-text">Boot War项目</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%88%9B%E5%BB%BAWar%E9%A1%B9%E7%9B%AE"><span class="toc-number">2.1.</span> <span class="toc-text">创建War项目</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%B5%8B%E8%AF%95War%E9%A1%B9%E7%9B%AE"><span class="toc-number">2.2.</span> <span class="toc-text">测试War项目</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%A4%96%E7%BD%AEtomcat%E6%9C%8D%E5%8A%A1%E5%99%A8"><span class="toc-number">2.2.1.</span> <span class="toc-text">使用外置tomcat服务器</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8%E5%86%85%E7%BD%AEtomcat%E6%9C%8D%E5%8A%A1%E5%99%A8"><span class="toc-number">2.2.2.</span> <span class="toc-text">使用内置tomcat服务器</span></a></li></ol></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Boot%E5%90%AF%E5%8A%A8%E6%B5%81%E7%A8%8B"><span class="toc-number">3.</span> <span class="toc-text">Boot启动流程</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#SpringApplication%E6%9E%84%E9%80%A0%E5%88%86%E6%9E%90"><span class="toc-number">3.1.</span> <span class="toc-text">SpringApplication构造分析</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#SpringApplication-run-%E6%96%B9%E6%B3%95%E5%88%86%E6%9E%90"><span class="toc-number">3.2.</span> <span class="toc-text">SpringApplication run()方法分析</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Tomcat%E5%86%85%E5%B5%8C%E5%AE%B9%E5%99%A8"><span class="toc-number">4.</span> <span class="toc-text">Tomcat内嵌容器</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Tomcat%E5%86%85%E5%B5%8C%E5%AE%B9%E5%99%A8-1"><span class="toc-number">4.1.</span> <span class="toc-text">Tomcat内嵌容器</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#Spring%E9%9B%86%E6%88%90%E5%86%85%E5%B5%8CTomcat"><span class="toc-number">4.2.</span> <span class="toc-text">Spring集成内嵌Tomcat</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#Boot%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%8E%9F%E7%90%86"><span class="toc-number">5.</span> <span class="toc-text">Boot自动配置原理</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#Boot%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%8E%9F%E7%90%86-1"><span class="toc-number">5.1.</span> <span class="toc-text">Boot自动配置原理</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%BB%8F%E5%85%B8%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E5%AE%9E%E7%8E%B0"><span class="toc-number">5.2.</span> <span class="toc-text">经典自动配置实现</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E8%87%AA%E5%8A%A8%E9%85%8D%E7%BD%AE%E7%B1%BB"><span class="toc-number">5.3.</span> <span class="toc-text">自定义自动配置类</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E8%A3%85%E9%85%8D%E5%BA%95%E5%B1%82"><span class="toc-number">6.</span> <span class="toc-text">条件装配底层</span></a></li></ol></div></div></div></div></main><footer id="footer"><div id="footer-wrap"><div class="copyright">&copy;2020 - 2023 By 狼族少年、血狼</div><div class="framework-info"><span>框架 </span><a target="_blank" rel="noopener" href="https://hexo.io">Hexo</a><span class="footer-separator">|</span><span>主题 </span><a target="_blank" rel="noopener" href="https://github.com/jerryc127/hexo-theme-butterfly">Butterfly</a></div></div></footer></div><div id="rightside"><div id="rightside-config-hide"><button id="readmode" type="button" title="阅读模式"><i class="fas fa-book-open"></i></button><button id="darkmode" type="button" title="浅色和深色模式转换"><i class="fas fa-adjust"></i></button><button id="hide-aside-btn" type="button" title="单栏和双栏切换"><i class="fas fa-arrows-alt-h"></i></button></div><div id="rightside-config-show"><button id="rightside_config" type="button" title="设置"><i class="fas fa-cog fa-spin"></i></button><button class="close" id="mobile-toc-button" type="button" title="目录"><i class="fas fa-list-ul"></i></button><button id="go-up" type="button" title="回到顶部"><span class="scroll-percent"></span><i class="fas fa-arrow-up"></i></button></div></div><div><script src="/js/utils.js"></script><script src="/js/main.js"></script><script src="https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.min.js"></script><div class="js-pjax"></div><script defer="defer" id="ribbon" src="https://cdn.jsdelivr.net/npm/butterfly-extsrc/dist/canvas-ribbon.min.js" size="150" alpha="0.6" zIndex="-1" mobile="false" data-click="false"></script><script async data-pjax src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script></div><div id="local-search"><div class="search-dialog"><nav class="search-nav"><span class="search-dialog-title">搜索</span><span id="loading-status"></span><button class="search-close-button"><i class="fas fa-times"></i></button></nav><div class="is-center" id="loading-database"><i class="fas fa-spinner fa-pulse"></i><span>  数据库加载中</span></div><div class="search-wrap"><div id="local-search-input"><div class="local-search-box"><input class="local-search-box--input" placeholder="搜索文章" type="text"/></div></div><hr/><div id="local-search-results"></div></div></div><div id="search-mask"></div><script src="/js/search/local-search.js"></script></div></body></html>